Swift optional Array property is immutable? - arrays

I am constructing an array of booleans to store the state of the sections in a UICollectionView. It is a variable stored as a property of my UIViewController:
var _weekSelections : Array<Bool>!
Then, in a function called by loadView(), I construct the array and assign a value to the first index:
_weekSelections = Array<Bool>(count:_weekCount, repeatedValue:false)
_weekSelections[0] = true
The value at index 0 remains false! The array is constructed, and has multiple elements, but any assignment that I make to an index does not affect the value stored at that index, even if I check the value on the very next line of code. I know that Swift makes a copy of an array if I perform an action that may change its length, but I don't think this is a case where a copy would me made. The only way I can get any value to change is if I manually create a copy as follows:
var copy = _weekSelections
copy[0] = true
_weekSelections = copy
Am I missing something obvious or could this be a strange bug?

For the sake of having my code on SO rather than Pastebin, here's my observation. This looks like some kind of bug or unexpected behaviour when using an optional array in a Swift class derived from an Objective C class. If you use a plain Swift class, this works as expected:
class Foo {
var weekSelections: Array<Bool>!
func test() {
weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]) // Prints "true"
}
}
var foo = Foo()
foo.test()
However, if you derive Foo from NSObject:
import Foundation
class Foo : NSObject { // This derivation is the only difference from the code above
var weekSelections: Array<Bool>!
func test() {
weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]) // Prints "false"
}
}
var foo = Foo()
foo.test()
Even in this case, if you do your weekSelections initialisation in an initialiser, then it works:
class Foo : NSObject {
var weekSelections: Array<Bool>!
init() {
weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]) // Prints "true"
}
}
var foo = Foo()
Personally, I'd say that this is a bug. I can't see anything in any documentation that would explain the difference in behaviour when derived from NSObject.
I also can't see anything that says that optional array properties would be immutable. This would be especially strange when you consider that "immutable" arrays are actually mutable in Swift, i.e. this:
// Use "let" to declare an "immutable" array
let weekSelections = Array<Bool>(count: 10, repeatedValue: false)
weekSelections[0] = true;
println(weekSelections[0]); // Prints "true"; arrays are never really "immutable" in Swift
...works fine, and is currently documented as being valid, even if it seems a bit odd.
Personally, I'd use whatever workaround you can and raise a bug with Apple, to see what light they can shed.

Not an explanation, but a workaround. The issue isn't with the count:repeatedValue initializer, but rather with the array being assigned to an optional variable. From what I can tell optional arrays can only use accessor methods and not mutator methods--effectively they're immutable. Temporarily assigning _weekSelections to a non-optional variable before attempting to change its contents (and assigning back to _weekSelections when done) will work. Please note that this seems to create a new array (with the same elements) on assignments, so there may be memory issues to consider if the array is very big. Of course, simply using a non-optional variable in the first place will also work.
As for why optional arrays aren't mutable, that may be a bug or there may be some esoteric reason for it I'm not fathoming. Anyone else have a plausible-sounding theory?

Related

How is Array() ambiguous when the given argument is a known type?

I'm seeing something that doesn't make sense to me. Here's my code...
import Foundation
enum SeparatorPaddingLocation{
case beforeSeparator
case afterSeparator
}
struct SeparatorToken{
let separator : Character
let formattedSeparator : String
let paddingLocation : SeparatorPaddingLocation
}
extension Array where Element == String {
func aligned(separatorTokens:[SeparatorToken]) -> [String] {
// This won't compile!
let remainingSeparatorTokens = Array(separatorTokens.dropFirst())
return self
}
}
The 'let' causes this compile error even though dropFirst() returns an ArraySlice<SeparatorToken>.
Type of expression is ambiguous without more context
Why is this ambiguous? What am I missing?
I also tried fully qualifying it like so, but this didn't work either...
let remainingSeparatorTokens:[SeparatorToken] = Array(separatorTokens.dropFirst())
Inside of generic extensions, type parameters bind to their scope. I'm not sure this is really a good thing (or possibly if it's really a bug), but it shows up consistently, so the type parameter here implicitly wants to be String.
A simpler example:
let x = Array() // error: generic parameter 'Element' could not be inferred
That makes sense. But put it in an extension:
extension Array {
func f() {
let x = Array()
}
}
This compiles, and x is of type [Element].
In your case, you've set Element to String, so the call to Array.init wants to create a [String] and it's getting confused because SeparatorToken != String. You need to pass the type parameter explicitly (as you've discovered):
let remainingSeparatorTokens = Array<SeparatorToken>(separatorTokens.dropFirst())
Again, I don't really consider this a feature in Swift, and it may even be considered a bug. But it's very consistent.
I just run your code in playground and updated as follows, then error is gone:
import UIKit
import Foundation
enum SeparatorPaddingLocation{
case beforeSeparator
case afterSeparator
}
struct SeparatorToken{
let separator : Character
let formattedSeparator : String
let paddingLocation : SeparatorPaddingLocation
}
extension Array where Element == String {
func aligned(separatorTokens:[SeparatorToken]) -> [String] {
// I update code here
let remainingSeparatorTokens = Array<SeparatorToken>(separatorTokens.dropFirst())
return self
}
}
As per swift, Array required type when you going to initialise with Array.
I hope this will work for you.
For more details about array visit: https://developer.apple.com/documentation/swift/array
This is not a bug, and there's nothing mysterious or bug-like going on.
Not a bug: it's the very reason the == operator is available in where clauses: to require Element to be of type String, as explained here. Search the page for "you can also write a generic where clauses that require Item to be a specific type".
Not bug-like: when you're inside the extension and call an Array initializer, the compiler takes your where clause to indicate what specialization of Array you want. Your where clause says String, so Array<String> it is. The compiler can't read your mind when you pass an ArraySlice<SeparatorToken> into an Array<String> initializer. Did you make a mistake, or did you really want an Array<ArraySlice<SeparatorToken>>? Hence the error message about ambiguity.
Here's a simplified and annotated version of OP's code, in case anyone finds it illuminating.
class Foo<T> {
var theFoo: T!
init() { theFoo = nil }
init(_ aFoo: T) { theFoo = aFoo }
}
extension Foo where T == Int {
func bar() {
// This one fails, because our where clause
// says this extension applies to Foo<Int>,
// but now we're trying to assign it to a Foo<String>
let aFoo: Foo<String> = Foo()
// This one also fails, for the same reason.
// It uses the other initializer, but we're
// still inside the Foo<Int> extension.
let bFoo: Foo<String> = Foo("bFoo")
// This one works, because we're expressly
// overriding the where clause
let cFoo: Foo<String> = Foo<String>("cFoo")
}
}
The ambiguity error makes sense. I told the compiler in my where clause to make this an Int extension, so it created Foo<Int>s. But then I tried to assign those to variables of type Foo<String>. The compiler couldn't tell whether I really wanted Foo<String> or I had made a mistake, so it gave up.

How to declare [Int] in optional

How to declare [Int] in optional
I try
var listofNumber = [Int]?()
But it's not working
But if I try
var listofNumber : [Int]?
The problem is I cant use listofNumber.append anymore
Value of type '[Int]?' has no member 'append'
You are writing [Int]? but this is just syntactic sugar.
Behind the scenes, what you're actually declaring isOptional<[Int]>.
If you look at the documentation for Optional<T>, you can see that there are three initialisers declared for this value type:
init(from: Decoder) throws This one is used in conjunction with the coding protocols introduced in Swift 4. It is of no relevance here.
init(nilLiteral: ()) This one initialises the Optional<T> to nil.
init(some: T) This one initialises the Optional<T> to T.
Your statement var listOfNumbers = [Int]?() does not match any of the initialisers and thus is not working.
Now what you probably want to do - and what most of us would presumably do here - is:
var listOfNumbers: [Int]? = []
This declares the type of listOfNumbers to be [Int]? (aka Optional<[Int]>) and at the same time sets its initial value to an empty array.
The crucial thing to note here is that virtually all of this is syntactic sugar. If you went out of your way and defined the var "natively", it would look something like this:
var listOfNumbers = Optional<[Int]>(Array<Int>()).
(You could leave out the generic parameter to Array, as it would be inferred by the type system here.)
Since this is cumbersome to write and also doesn't read quite well, you can use the short version.
As for the second issue regarding Array.append.
Your var is of type [Int]? (aka Optional<[Int]>) and thus you cannot access vars or call methods defined on the Array type. You need to unwrap the optional first.
listOfNumbers?.append(4711).
If you're using Xcode, it should actually provide you with a fix-it for this kind of issue.
Now since we were talking about "explicitness" anyway:
listOfNumbers?.append(4711) is again a shiny example for syntactic sugar.
Explicitly written, it would look something like this:
// Optional<T> is actually an enum, so we can switch over it.
switch listOfNumbers {
case .some(var value):
value.append(4711)
case .none:
break
}
// Alternative
if case .some(var value) = listOfNumbers {
value.append(4711)
}
As you can see, this is quickly getting bloated, especially considering optional chaining, etc. Hence, the short version was "invented".
I've written this long answer to make it clear to you what's going on behind the scenes here and I guess it never hurts to remind yourself of the nuts and bolts you’re building upon sometimes.
Update
Note that it would be perfectly valid for you to write the statement like this:
var listOfNumbers = [Int]?([])
or
var listOfNumbers = [Int]?(nilLiteral: ())
The amount of syntactic sugar you are using it up to you and you can mix it with "explicit" coding however you want.
For declaring use this syntax:
var listOfNumber: [Int]? = []
Then use any method you need:
listOfNumber?.append(20)
listOfNumber?.insert(40, atIndex: 1)
listOfNumber! // result: [20,40]
Second part.
Here's your code:
if listOfNumber?[0] == nil {
print("if access...")
} else {
listOfNumber?.append(20)
print(listOfNumber!)
}
var listofNumber : [Int]? is the good approch.
Dont forget to init using
var listofNumber : [Int]? = [] or var listofNumber : [Int]? = nil
After, as the array is optional, use listofNumber?.append(10).

Is there a way to override Array to String casting in Swift?

I'm playing around with Swift trying to make it look more "dynamically typed" – just for fun, no production value expected.
Now I'm stuck with overwriting behavior of converting builtin types to String.
For example, I'd like to see this output for Array:
let nums = [1, 2, 3]
print(nums) // "I'm an array"
So far I tried to
make an extension to NSArray (not compiles)
implement CustomStringConvertible (not compiles)
make an extension to Array (compiles, changes nothing)
Seems like I'm on the wrong path:
extension Array: CustomStringConvertible {
public var description: String { return "An array" }
}
gives the warning:
Conformance of 'Array' to protocol 'CustomStringConvertible' was already stated in the type's module 'Swift'
Is this doable in Swift?
This does not work because Array overrides description. If array did not override it then it would print "An array". The class method 'wins' over the extension.
extension Array {
public var description: String { return "An array" }
}
You could create a Wrapper class for your array. It's a workaround but doesn't override array's description itself.
class ArrayWrapper<T> : CustomStringConvertible{
var array : Array<T> = Array<T>()
var description: String { return "An array" }
}
You could then use it like this.
var array = ArrayWrapper<Int>()
array.array = [1,2,3]
print(array) //prints "An Array"
print(array.array) //still prints "[1, 2, 3]"
Seems like it is not possible to neither subclass nor overwrite a builtin Array type.
We could use a wrapper though (credits to #Yannick) and implement a ArrayLiteralConvertible protocol, so we could use square brackets for initialization.
struct Array<T> {
let array: [T]
}
extension Array: ArrayLiteralConvertible {
init(arrayLiteral elements: T...) {
self.array = elements
}
}
extension Array: CustomStringConvertible {
var description: String { return "An array" }
}
let array = [1,2,3]
print(array) // "An array\n"
Swift does not allow you to provide a protocol override for a type that already declares that protocol:
Imagine that you have a set of integers and you want to override the default implementation of CustomStringConvertible for a Set
The simplest answer to all of this is: you can't, and that's a feature not a bug. Set isn't your type and you don't get to change its already-defined behavior like this.
The capability to intercept and alter the behavior of a type to do something different to what that type is originally designed to do is powerful, but fraught with downsides. In this case, you've chosen a fairly benign thing to alter, but in other cases doing this could do all sorts of damage to assumed invariants of a type by changing its program-wide behavior.
The best way to alter a type to do something different like this is to wrap it in your own low-cost struct. Unfortunately this does mean writing a fair amount of boilerplate for forwarding – though less and less these days as we gain features like synthesized conformances and dynamic member lookup. Hopefully someday we'll get features that do make easier to create a newtype with customized behavior.
https://forums.swift.org/t/whats-the-best-way-to-override-customstringconvertible-for-a-collection/24844

Swift 2 - Check Type of empty Array (Introspection)

I'm currently working on introspection in Swift 2 and have Problems getting the specific type for an Array (in this example an Array<String>).
var prop = obj.valueForKey("strings")!
if prop is Array<String> {
println("true")
}
if prop is Array<Int> {
println("true")
}
Output is:
true
true
while it should be
true
false
Is there a way to find out the type for the members of the Array? For example, if I daclared the Array as Array<String> I want to get String or at least be able to check if it is.
MirrorType also did not lead to any success on that by now.
There are 2 ways to achieve what you want:
if prop.dynamicType == Array<Int>.self (or [Int].self) which is better than if prop.dynamicType == [Int]().dynamicType { because [Int]() creates an unused instance of "array of integers".
Typically, when you check if an array is specific-typed, you plan to use it
in a certain way (as a result, you will likely cast your array to
[Int]). Having that said, I recommend using if let arrayOfInts =
prop as? Array<Int> {. Using this construct, you will check for
type compatibility and prepare your array to be treated in a special way (using the casted arrayOfInts reference).
Anyway, it's up to you to decide what to do.
Perhaps what you want is the type of each individual item inside the Array rather than the type of the Array itself? If you are using collection types in Swift, all the items stored in the Array (or Dictionary) are of the same type (except if you declare the Array as Array for example to break the rules... which is not usually necessary, or wanted).
By declaring an Array with its initially values you are automatically telling the compiler what type they are. If you do something like this:
let obj = [1,2,3]
var property = obj[0]
if property is String {
print("true")
}
if property is Int {
print("true")
}
The compiler will already tell you that property is String always fails, and there is actually no need to do that test (because we already know that it will always fail).
If you are working with Objective-C APIs and types on the other hand, there may be occasions where you will need to test for type, this is a good example of testing for type in an Objective-C collection that has items of different types:
let userDefaults = NSUserDefaults.standardUserDefaults()
let lastRefreshDate: AnyObject? = userDefaults.objectForKey("LastRefreshDate")
if let date = lastRefreshDate as? NSDate {
print("\(date.timeIntervalSinceReferenceDate)")
}
Hope this helps.

How to create a mutable collection type like Array?

I'm trying to code a type to represent a pointer in GPU device, and it should act like an array, with indexed property for get and set. I get no problem if the element type is a primitive type, but when I use a struct, I cannot change its member's value.
See this code:
#nowarn "9"
open System
open System.Runtime.InteropServices
[<Struct;StructLayout(LayoutKind.Sequential)>]
type MyStruct =
val mutable x : int
val mutable y : int
val mutable z : int
override this.ToString() = sprintf "(%d,%d,%d)" this.x this.y this.z
let deviceOnly() = failwith "this function should be used in quotation only"
type DevicePtr<'T>(h:nativeint) =
member this.Handle = h
member this.Item with get (idx:int) : 'T = deviceOnly() and set (idx:int) (value:'T) : unit = deviceOnly()
member this.Reinterpret<'T2>() = DevicePtr<'T2>(h)
member this.Volatile() = DevicePtr<'T>(h)
static member (+) (ptr:DevicePtr<'T>, offset:int) = DevicePtr<'T>(ptr.Handle + nativeint(offset * sizeof<'T>))
let test() =
let mutable test1 = MyStruct()
test1.x <- 1
let foo (collection:MyStruct[]) =
collection.[0].x <- 1
let bar (collection:DevicePtr<MyStruct>) =
collection.[0].x <- 1
//error FS0257:
// Invalid mutation of a constant expression.
// Consider copying the expression to a mutable local, e.g. 'let mutable x = ...'.
So, the type is DevicePtr<'T>, and it has indexed property Item with both get and set method. but the get method just return a value of 'T, so I cannot mutate it. But the system array works.
Anyone has some experience like this? to create a type works like array? which I hope the get function of the indexed property return a mutable ref instead of a value.
You cannot create a type that works with structures like an array because the language and runtime give special handling to arrays that other classes do not get.
With an array, an expression accessing an element that is a structure, as in your foo function, causes the element to be directly modified in place. The collection.[0] is effectively setting up a "pointer" or "reference" to an element that the .x expression is then applied to, allowing you to manipulate the object in place.
For other classes, the indexer is just another function, meaning a copy of the value is being returned. So in the bar function, the collection.[0] creates a copy of the value returned instead of the reference you get from the array. Because the .x would be modifying the temporary copy, the compiler issues the error you see. (A very similar warning would occur in C# or VB for similar code.) As the message suggests, you will need to create a variable to hold the copy, modify the copy, and assign it back to collection.[0].

Resources