'[Any?]' is not convertible to 'Anyobject?' - arrays

i found this error in this line : if caching === nil on my code:
struct dataArray {
var dataById = Dictionary<String, Any>()
}
var id :String?
func dataBegin() {
let idString = id as String!
let byCategory = [dataArray().dataById[idString]]
if byCategory === nil { //error:'[Any?]' is not convertible to 'Anyobject?'
// some code
}
}

=== is the identity operator, which is supposed to be used for checking if two references point to the same instance. You probably wanted to use == to check for nil.
However, the byCategory variable is initialized as a non optional array, and as such it cannot be nil, and consequently it cannot be checked for nil.
I think there's a mistake in the logic of that function.

Related

Why (Array<Character> = []) is Array<Bool> == true

If the defined array is empty, why the data type inside the array does not work
I create a empty Character Array, and I confused that why the result of a is Array<Bool> is true.
In fact, i tried a is Array<T> (T: Bool, String, Int...), all of them is true.
var a: Array<Character> = []
a is Array<Bool>
The result is:
true
After some digging, I found that this is the intended behavior.
Swift checks whether an array can be downcast by attempting to downcast all of the source elements to the destination element type. Since an empty array contains no elements, that means down casting it to another array with a different element type is always successful.
Below are swift's code for handling the as? operator. I doubt that swift handles the is operator differently.
public func _arrayConditionalCast<SourceElement, TargetElement>(
_ source: [SourceElement]
) -> [TargetElement]? {
var successfulCasts = ContiguousArray<TargetElement>()
successfulCasts.reserveCapacity(source.count)
for element in source {
if let casted = element as? TargetElement {
successfulCasts.append(casted)
} else {
return nil
}
}
return Array(successfulCasts)
}
https://github.com/apple/swift/blob/main/stdlib/public/core/ArrayCast.swift (line 74)
Sources
A similar bug report an swift.org: SR-7738
Some convincing explanation: SR-6192

Delete struct from array of structs if struct string element is nil or empty swift

Lets say I have a simple struct as such:
struct Model: Codable {
var myVariable: String
var myVariable2: String?
}
And lets say sometimes, myVariable2 is ether a nil/null value ( from json) or a literal empty string as such "".
How would I filter out the empty nil/null and remove that particular struct from the array of structs?
I have tried:
Model.compactMap{ $0.myVaraible2 }.flatMap { $0 }
to filter out the specific string. But obviously I don't get my desired result because It's wrong. so, to first filter out the nil/empty value and then remove that struct from the array, it should be pretty straight forward yes?
Can I filter it out right after I call JSONDecoder?
let model = try JSONDecoder().decode([Model].self, from: data)
something like : I know this next line of code isn't good. =)
for element in model {
if element.myVariable2.isEmpty || element.myVariable2 == "" {
model.remove(at: what to put here ? )
}
I know the for loop is BAD !!! but how would I fix it or do something more swifty ?
Thanks!
The tool you want is filter:
let filteredModels = model.filter { $0.myVariable2 != nil }
This removes all elements which have a nil for this property. The property is still Optional, however, since you defined it that way. If you also want to check for "" you can add that to the predicate:
let filteredModels = model.filter { $0.myVariable2 != nil && $0.myVariable2 != "" }

Swift - C API bridge - how to handle null pointers

In Swift I am using C API that returns struct with char array (containing UTF8 null terminated string or null).
struct TextStruct {
char * text;
//other data
}
I use:
let text: String = String(cString: data.text)
This works, however, when data.text is nullptr, this fails with
fatal error: unexpectedly found nil while unwrapping an Optional value
Is there any workaround, or I have to check data.text manually before using cString ctor?
In addition to Gwendal Roué's solution: You can
annotate the C API to indicate whether the pointer can be null or not.
For example,
struct TextStruct {
char * _Nullable text;
//other data
};
is imported to Swift as
public struct TextStruct {
public var text: UnsafeMutablePointer<Int8>?
// ...
}
where var text is a "strong" optional instead of an implicitly
unwrapped optional. Then
let text = String(cString: data.text)
// value of optional type 'UnsafeMutablePointer<Int8>?' not unwrapped; ...
no longer compiles, and forces you to use optional binding or
other unwrapping techniques, and the "fatal error: unexpectedly found nil"
cannot happen anymore accidentally.
For more information, see "Nullability and Objective-C" from the Swift Blog –
despite the title, it can be used with pure C as well.
Yes, you have to check data.text, in order to make sure it can feed the String(cString:) initializer, which is documented to require a non-null pointer.
A technique is to use a if let statement. This is a classic technique for safely unwrapping optional values:
let str: String?
if let ptr = ptr {
str = String(cString: ptr)
} else {
str = nil
}
print("got \(str ?? "nil")")
Another technique is the Optional.map function:
let str = ptr.map { String(cString: $0) }
print("got \(str ?? "nil")")
You can check if the address of the pointer is set
let textIsSet = Int(bitPattern: data.text) != 0
let text: String? = textIsSet ? String(cString: data.text) : nil

How to append to an array that is a value in a Swift dictionary

Let's assume I have a dictionary that accepts a string as the key and an array as value:
var d = [String: [Int]]()
d["k"] = [Int]()
Now I would like to append() to the array that is under the ["k"]. How do I do that?
[Int](d["k"]).append(1) // error
(d["k"] as [Int]).append(1) // error
I guess this is something really basic but I cannot figure it out... thanks a lot!
Because Dictionary and Array are both struct types, you can't modify the Array in place, but instead have to extract and restore it:
if var items = d["k"] {
items.append(1)
d["k"] = items
}
else {
d["k"] = [1]
}
If the element for that key exists, you can simply do:
d["k"]?.append(1)
Note that if no element exists for the "k" key, then nothing happens. If you want to ensure there's actually an array for that key, just add this before appending:
if d["k"] == nil {
d["k"] = []
}
Like this:
var d = [String: [Int]]()
d["k"] = [Int]()
if var items = d["k"] {
items.append(1)
d["k"] = items
}
You have to:
pull it out
unwrap the Optional
assign it to a var reference
append, and
put it back again
The if var line does the first three steps all in one.
All other answers are correct, but If you feel annoying to check nil, unwrap ...
I made a custom operator.
With this, you can unconditionally expand Optional arrays
infix operator +=! {
associativity right
precedence 90
assignment
}
func +=!<T, S:SequenceType where S.Generator.Element == T>(inout lhs:[T]?, rhs:S) {
if lhs == nil {
// the target is `nil`, create a new array
lhs = Array(rhs)
}
else {
// the target is not `nil`, just expand it
lhs! += rhs
}
}
Usage:
var d = [String: [Int]]()
// ... d["test"] may or may not exists ...
d["test"] +=! [1]

How to tell if a variable is an array

I have a Swift function that accepts Any and I want it to be able to accept an array of Strings, an array of Ints, a mixed array, or an array of arrays, etc. It also can accept just a String or an Int, etc, not in an array.
So I have this:
private func parse(parameter: Any) {
if parameter is Int {
// Int
} else if (parameter is Float) || (parameter is Double) {
// Double
} else if parameter is String {
// String
} else if parameter is Bool {
// Bool
} else if let array = parameter as? [Any] {
// Should catch all Arrays
} else {
assert(false, "Unsupported type") // [String] ends up here
}
}
But if I call parse(["Strings"]), the assert is raised. How can I catch all types of Arrays?
edit - there was some confusion as to what I'm trying to accomplish. I basically need to return a String based on the type, so Int -> "" and String -> "", so an array would make recursive calls to return "..."
This post is marked as a duplicate, but that other question is about Javascript, not Swift.
I finally found the way to do that, which is to use NSArray for casting.
private func parse(x: Any) {
if let o = x as? [Any] {
println("[Any]")
}
if let o = x as? [AnyObject] {
println("[AnyObject]")
}
if let o = x as? NSArray {
println("NSArray")
}
}
let a: [Any] = ["bar"]
let b: [AnyObject] = ["bar"]
let c = ["foo", 3.14]
parse(a) // ==> [Any]
parse(b) // ==> [AnyObject], and also NSArray
parse(c) // ==> NSArray
It look so that an array containing values of Any internally represented in NSArray.
(But should it be able to cast c to [Any]...? I'm suspecting it's a bug.)
The key to understanding typing and type related issues in Swift is that all roads lead to protocols.
The challenge of this problem is detecting any type of array, not just one concrete type. The OP's example failed because [Any] is not a base class or a generalized pattern of [String], that is to say, that (from what I can tell), in Swift [T] is not covariant on T. Beyond that, you cannot check for SequenceType or CollectionType since they have associated types (Generator.Element).
The idiomatic solution is thus to use a marker protocol to indicate which types you want to match your criteria. As illustrated below, you achieve this by creating an empty protocol, and associating it with the types of interest.
import Foundation
protocol NestedType {}
extension Array: NestedType {}
extension Set: NestedType {}
extension Dictionary: NestedType {}
extension NSSet: NestedType {}
protocol AnyTypeOfArray {}
extension Array: AnyTypeOfArray {}
extension NSArray: AnyTypeOfArray {}
protocol AnyTypeOfDictionary {}
extension Dictionary: AnyTypeOfDictionary {}
func printType(v:Any) {
if v is NestedType {
print("Detected a nested type")
}
if v is AnyTypeOfArray {
print("\t which is an array")
}
else if v is AnyTypeOfDictionary {
print("\t which is a dictionary")
}
}
printType([String:Int]())
printType([Int]())
printType(NSArray())
The output of which is:
Detected a nested type
which is a dictionary
Detected a nested type
which is an array
Detected a nested type
which is an array
One way you can do this is to separate the function out to two separate implementations (with the same name), one that takes anArray and one for everything else. You'll also need to make them generic functions instead of using the Any type. With that setup, Swift can use type inference to figure out the best function to call.
I'd implement it something like this (I'm just printlning the type to show where things end up):
func parse<T>(parameter: T) {
if parameter is Int {
println("Int")
} else if (parameter is Float) || (parameter is Double) {
println("Double")
} else if parameter is String {
println("String")
} else if parameter is Bool {
println("Bool")
} else {
assert(false, "Unsupported type")
}
}
func parse<T>(parameter: Array<T>) {
println("Array")
for element in parameter {
// Recursively parsing...
parse(element)
}
}
Then calling it like this:
parse(1) // Int
parse(0.1) // Double
parse("asdf") // String
parse(true) // Bool
parse(["asdf", "asdf"]) // Array -> String String
Outputs:
Int
Double
String
Bool
Array
String
String
You can use the _stdlib_getTypeName that returns the mangled type name for the given value.
For example:
var myString = "String"
var myInteger = 10
var myArray = [10,22]
var myDictionary = ["one": 1, "two": 2, "three": 3]
println("\(_stdlib_getTypeName(myString))")
println("\(_stdlib_getTypeName(myInteger))")
println("\(_stdlib_getTypeName(myArray))")
println("\(_stdlib_getTypeName(myDictionary))")
The result will be:
_TtSS // for String
_TtSi // for integer
_TtSa // for array
_TtVSs10Dictionary // for dictionary

Resources