How to keep the reference to an array after manipulation? - ios

How do I keep the reference to an array after an items is appended?
Updated code example, because the prior example didn't seem to be clear enough.
class ViewController: UIViewController {
var numbers = Numbers.singleton.numbers
override func viewDidLoad() {
print(numbers.count)
Numbers.singleton.add(1)
print(numbers.count) // prints 0
print(Numbers.singleton.numbers.count) // prints 1
}
}
class Numbers {
static let singleton = Numbers()
var numbers: [Int]!
private init() {
numbers = []
}
func add(number: Int) {
numbers.append(number)
}
}

Arrays in Swift don't have "references". They are structs, and a struct is a value type. Your (badly named) arrayRef is a separate copy, not a reference to self.array.
Moreover, there is no good reason to want to do what you (seem to) want to do. To have two simultaneous references to a mutable array would be unsafe, since the array can be changed behind your back. The Swift design is sensible; use it, don't subvert it.

Related

Make `Collection` protocol unique by Element property

I have been trying to make this extension generic, without any success. Any ideas how to improve it? (I would like to get rid of the hardcoded WKBackForwardListItem and url part and just accept any property) But I am afraid this is not possible just yet?
extension Collection where Iterator.Element: WKBackForwardListItem {
func unique() -> [Iterator.Element] {
var seen: [Iterator.Element] = []
for element in self {
if seen.map({ $0.url }).contains(element.url) == false {
seen.append(element)
}
}
return seen
}
}
Ideally the signature will end up like this
func unique(by property: Property) -> [Iterator.Element] {
Update
Given the nature of the array, an WKWebView history. It is very important to preserve the order of the array. So every use of Set is out of the question.
Update 2
Resulting code was, I lost the Collection protocol, but that is ok for this example.
extension Array where Iterator.Element: Equatable, Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
return NSOrderedSet(array: self).array as! [Iterator.Element]
}
}
Thanks!
You don't need to create this extension of Collection. What you need is a Set. The only thing you need to make sure is that the items in your array conform to the Equatable protocol.
Sample:
let food = ["taco", "apple", "pizza", "taco"]
let uniqueFood = Set(food) // ["taco", "apple", "hot dog"]
Then all you need to do is create an Array from that Set:
let foodArray = Array()
For your use case WKBackForwardListItem does conform to Equatable so you won't need to do anything special for that.
In order to maintain the order of your data Apple has provided NSOrderedSet. This has the uniqueness that Set offers while maintaining the order Array provides.
I hope this helps.
You're looking for NSOrderedSet (and NSMutableOrderedSet). It has the uniqueness of Set with the ordering of Array! See Apple's documentation.

how to make a deep copy of a swift array of class objects

So I know that swift array is implemented as struct, and it will do automatically copy by itself.
I have write my class MyClass and I have implemented copyWithZone to make copy of it
However, my swift array contains my MyClass objects, like:
var array = [MyClass]()
when I wan to make a copy of that array like
var anotherArray = array
It still does not call MyClassObject.copyWithZone, and later if I change the object property in array, anotherArray will also reflect the change.
How can I make a copy of that without writing a for loop to iterate every object?
It's not duplicated as deep copy for array of objects in swift because I cannot use struct to rewrite my class.
As a simple statement, you could use code like this:
var copiedArray = array.map{$0.copy()}
Note that the term "deepCopy" is a little misleading for what you're talking about. What if the array is heterogeneous and contains other containers like arrays, dictionaries, sets, and other custom container and "leaf" objects? What you should really do is to create a protocol DeepCopiable and make it a requirement that any object that conforms to DeepCopiable require that any child objects also conform to the DeepCopiable protocol, and write the deepCopy() method to recursively call deepCopy() on all child objects. That way you wind up with a deep copy that works at any arbitrary depth.
If Element of an array is of reference type, conform that Element with NSCopying and implement copyWithZone method.
class Book {
var name: String
init(_ name: String) {
self.name = name
}
}
extension Book: NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
return Book(name)
}
}
Now implement an array extension to make a deep copy
extension Array where Element: NSCopying {
func copy() -> [Element] {
return self.map { $0.copy() as! Element }
}
}
Test the code base
let b1 = Book("Book1")
let b2 = Book("Book2")
let list = [b1, b2]
let clonedList = list.copy()
clonedList is now copy of list and won't affect each-other if you make change to any of two.
Thanks Duncan for your solution, here is an extension using it.
Note that your class must adopt the NSCopying protocol. For how to do that see here https://www.hackingwithswift.com/example-code/system/how-to-copy-objects-in-swift-using-copy
extension Array {
func copiedElements() -> Array<Element> {
return self.map{
let copiable = $0 as! NSCopying
return copiable.copy() as! Element
}
}
}

Swift 2 Struct with Array

I am new to Swift and to making games (as well as fairly new to coding in general), I currently want to pass a variable between two Gamescenes. After doing some research I have come to the conclusion I require a struct with a [int]() to hold the numbers I want for some reason while I am not able to assign values into the my integer array generatedSequenceArray
my Struct:
struct generatedSequenceForGameScene {
static var generatedSequenceArray = [Int]()
}
In my one of my GameScene classes I have initialized my generatedSequenceForGameScene struct object:
var generatedSequence = generatedSequenceForGameScene()
The method I am calling on is:
func pickNumbers(){
for var x = 0; x < highLevel; ++x{
generatedSequence.generatedSequenceArray.append(Int(arc4random_uniform(10)))
print(generatedSequence.generatedSequenceArray)
}
}
Before attempting to have a variable move from each GameScene my code was this:
Variable:
var generatedSequence = [Int]()
Method:
func pickNumbers(){
for var x = 0; x < highLevel; ++x{
generatedSequence.append(Int(arc4random_uniform(10)))
print(generatedSequence)
}
}
This gave me my desired results.
NOTE: The pickNumber method gets called in a method named loadView() which is placed in the didMoveToView method in the program
There are several things to note here.
First, the static keyword inside your struct makes generatedSequenceArray a static property. This means it'll be the same everywhere you call it. This isn't what you want.
Second, unless you plan to add more properties to your struct, there's no reason for it to exist. Why wrap it in a struct at all? Why not just pass the array of integers (your picked numbers) directly?
Third, type names should be proper case. So generatedSequenceForGameScene should be GeneratedSequenceForGameScene).
I'm not sure what exactly isn't working for you. I guess you're having trouble sharing a sequence from one game scene to another. If that's the problem, maybe the following helps:
struct Sequence {
var seqArray = [Int]()
mutating func pickNumbers() {
for var x = 0; x < 10; ++x {
seqArray.append(Int(arc4random_uniform(10)))
}
print( "picked: \(seqArray)" )
}
}
class GameScene {
var s = Sequence()
}
var g1 = GameScene()
var g2 = GameScene()
g1.s.pickNumbers()
g2.s.seqArray = g1.s.seqArray
print( g2.s.seqArray )

Extending Lists in Swift

I'm developing a backend part of a system using Apple's Swift that needs to deal with email lists (an array of Email objects) and I need to set my own properties to this list such allowRepeated: Bool, and some methods like performCleanup() to remove duplicate entries if the allowRepeated property is set to true.
I have done something like that in a playground trying to follow the documentation notes about extending NSMutableArray class:
class EmailList: NSMutableArray {
override var count: Int {
get {
// It seems that the line below generates a infinity loop
return NSArray(array: self).count
}
}
override func insertObject(anObject: AnyObject, atIndex index: Int) {
insertObject(anObject, atIndex: index)
}
override func removeObjectAtIndex(index: Int) {
super.removeObjectAtIndex(index)
}
override func addObject(anObject: AnyObject) {
super.addObject(anObject)
}
override func removeLastObject() {
super.removeLastObject()
}
override func replaceObjectAtIndex(index: Int, withObject anObject: AnyObject) {
super.replaceObjectAtIndex(index, withObject: anObject)
}
override func objectAtIndex(index: Int) -> AnyObject {
return super.objectAtIndex(index)
}
}
It seems the line return NSArray(array: self).count is causing an infinite loop. Why is this happening?
Cocoa collection classes (such as NSMutableArray) are what's known as "class clusters" and are not meant to be subclassed. I guarantee any malfunctions are due to subclassing NSMutableArray.
In your case, I'm guessing when your strange count override tries to create an array with its own contents, it's once again asked for its count when the Array(array:) constructor is called, since Array(array:) presumably needs to know how long it'll be based on the passed source array. Lather, rinse, repeat.
The real question is why would you short-circuit a basic Cocoa class in this way? What possible gains do you feel you're getting by causing the creation of a whole new array when NSMutableArray.count works perfectly fine? Don't do things like this.
Indeed, NS(Mutable)Array isn't meant to be subclassed, and doing it in Swift is a recipe for disaster.
I recommend that you either write your own array class which uses a Swift Array as it's underlying type - a wrapper basically, or you just write external functions which take arrays as arguments. You could also do this with an NSMutableArray, but that would likely be unnecessary.

Extending Array to append SKTextures [duplicate]

This question already has answers here:
Is it possible to make an Array extension in Swift that is restricted to one class?
(4 answers)
Closed 7 years ago.
Being fairly new to Swift I decided I would look at extending Array (or more specifically [SKTexture] Arrays of SKTexture) with a function to add a specified number of frames from the application bundle.
// FRAMES
FuzzyRabbit_0001#2x.png
FuzzyRabbit_0002#2x.png
FuzzyRabbit_0003#2x.png
FuzzyRabbit_0004#2x.png
// CALL
var rabbitTextures = [SKTexture]()
self.rabbitTextures.textureFromFrames("FuzzyRabbit", count: 4)
My first attempt is listed below, I am getting the error Cannot invoke 'append' with an argument list of type '(SKTexture!)' which from looking at the function fuzzyPush is because I am trying to append an SKTexture rather than the generic T.
Is this possible, or am I limited by the fact that I don't want the function to be generic but rather specific to Arrays of SKTexture.
extension Array {
// ONLY SKTexture
mutating func textureFromFrames(imageName: String, count: Int) {
if !(self[0] is SKTexture) { return }
for index in 1...count {
let image = String(format: "\(imageName)_%04d", index)
let texture = SKTexture(imageNamed: image)
self.append(texture) // ERROR: Cannot invoke append with an argument list of type SKTexture!
}
}
// WORKS FINE
mutating func fuzzyPush(newItem: T) {
self.append(newItem)
}
}
I was just curious if this is something I could do with an extension, its not a problem as I have this as a function that takes 3 parameters (imageName, count, arrayToAppend) so I can quite easily use that.
This extension is not possible to write today. You cannot apply an extension method to only certain types of arrays.
There are two good solutions. You can use a HAS-A pattern by creating a struct (TextureList) that contains a [SKTexture], or you can use a function.
You can replace :
self.append(texture)
with
self.append(texture as T)
I checked this on an array of strings though and it worked.
About the first check add another check to see if the array is empty otherwise the self[0] is SKTexture will fail.
This is the code I tested on an online swift compiler (SKTexture was not available obviously) :
extension Array {
mutating func textureFromFrames(imageName: String, count: Int) {
for index in 1...count {
let image = String(format: "\(imageName)_%04d", index)
self.append(image as T)
}
}
}
var arr = Array<String>()
arr.textureFromFrames("testing", count:4)
for tmp in arr {
println("\(tmp)")
}

Resources