Array of entity does not mutate in swift? - arrays

User Entity Model-
class UserEntity: NSObject {
var isAlreadyUser:Bool
init(isAlerdy:Bool){
isAlreadyUser = isAlerdy
}
}
App Delegate / Global Array
let new = ["F","E","D","C","B","A"]
for obj in new{
arrUser.append(UserEntity(isAlerdy: false))
}
VIEW CONTROLLER
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let home = Array(appDelegate.arrUser)
home[0].isAlreadyUser = true
print(appDelegate.arrUser[0].isAlreadyUser)
After I edit the local home array and update isAlreadyUser to true from false. This also changes in global array. Even I am mutating any making a copy of global array it still changes it i both the array.
I think some thing is wrong with entity. It is strong and not changing according to local scope of array.
Help me out.
EDIT:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var areAlreadyUsers:[UserEntity] = []
areAlreadyUsers = Array(appDelegate.arrUser)
areAlreadyUsers[0].isAlreadyUser = true
print(appDelegate.arrUser[0].isAlreadyUser)
Still no help. My global array is still changing.

If you were to make the UserEntity into a struct, you would not have the problem. Does it need to be an NSObject if not try using struct instead of class. This way you get copy on write behavior.

Problem:
The elements of the array are referenceType hence only the pointers to the objects will be copied. Hence modifying them will reflect in both the places.
Solution:
Copy each item contained in the array.
you can do some thing like, let localArray = globalArray.map {
$0.copy() }. Please note that it is important your object should
implement any of the copy methods such as copyWithZone.
Use a value type such as struct instead of class.

The point is that arrUser and home just contain pointers to the same UserEntity objects, which are unique (not copied).
So what you do you just change a value of a UserEntity property and it will obviously be reflected everywhere.
What you want to do is to rethink your app architecture, why would you mutate this value at all? For instance, you can create an array
var areAlreadyUsers:[UserEntity] = []
and just save there pointers to users that you would give true value. This way this information isn't saved anywhere.

Related

What is the best and quickest way to save a large Class Array in existing project? Realm isn't working

First off, I'm a relative amateur with app design having taught myself high-level swift/xcode last year, so apologies for my code in advance!
I've developed a game which has an array of a 'Player' Class called playerList. I currently convert this playerList array to JSON via Encoder and then save to device...however as my array grows, this exercise is beginning to take a long time, so I'm looking for an alternative. I presume the best solution is to rewrite the app to use CoreDate, SQLite etc, but I'm looking for a quick solution for now.
I could have used userDefaults, however steered away from this as large array and am instead trying to fudge a solution using Realm.
I've attempted the below, but whenever I look at my playerList after loading it is empty. Am I missing something obvious here, or alternatively is there a much better approach than using Realm?
class PlayerArray: Object {
var iden: Int = 0
var allThePlayers: [Player] = playerList
}
func saveViaRealm() {
// Get the default Realm
let realm = try! Realm()
// Define player list
let realmPlayerList = PlayerArray()
realmPlayerList.allThePlayers = playerList
realmPlayerList.iden = 1
// Write to realm
try! realm.write {
realm.add(realmPlayerList)
}
}
func loadViaRealm() {
// Get the default Realm
let realm = try! Realm()
//Retrieve objects from realm
let realmOutputPlayerList = realm.objects(PlayerArray.self)
// Filter to iden required
let realmFiltered = realmOutputPlayerList.filter{$0.iden == 1}[0]
// Assign to playerList
playerList = realmFiltered.allThePlayers
}
I would take a read through the Realm documentation once more around Lists and declaring variables. In your object class are you getting any errors? RealmSwift should be declared with #objc dynamic vars. Also, you shouldn't need but one let = realm. Here is the link to Realm.io documentation.

How do I store array of custom objects in core data?

Core data is still a bit new for me, so I don't quite understand the ins and outs. I understand how to save the basics like Strings, Ints, etc in core data, but I don't quite understand how to save an array of custom objects into core data, or if that is even possible. From my research, my current understanding is I need to set the attribute to Binary Data, and set its identifier to [exercise] (making a fitness application). Unfortunately when I try to pass my array into the managed context, I get an error in my code that reads "Cannot assign value of type '[Exercise]' to type 'Data?'"
func save (completion: (_ finished: Bool) -> ()){
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let workout = Workout(context: managedContext)
workout.nameOfWorkout = workoutNameField.text
workout.exercises = exercises // error appears on this line, both "exercises" are arrays
}
I guess my question is, is what I am attempting to do even possible? If so, what steps am I missing? I read somewhere to convert the array into NSData, and change it back when it needs to be accessed, but my concern is that when I try to change it back, it won't work as planned. Sorry for the long winded question, just want to make sure I'm including all details I can think of.
For storing custom types in CoreData, the fastest way is:
making your custom types (Exercise) subclass of NSObject
setting the attribute's type in the core data model to Transformable
setting the CustomClass to [Exercise]
So you have to define your Exercise class similar to:
public class Exercise: NSObject {
let name: String
let duration: TimeInterval
init(name: String, duration: TimeInterval) {
self.name = name
self.duration = duration
}
}
Then you go to the model definition and set the attribute type and CustomClass field:
CoreData Model Example
You can now use your save function as:
func save (completion: (_ finished: Bool) -> ()){
guard let managedContext = appDelegate?.persistentContainer.viewContext else { return }
let workout = Workout(context: managedContext)
workout.nameOfWorkout = workoutNameField.text
workout.exercises = exercises
}

Best way to utilize retrieved object's properties to populate arrays and subsequent labels?

I have a database (parse-server) from which I can fetch objects which contain information. Some of the information in the properties of the objects are used to populate labels on table views. The way I have been populating, let's say, the userName and userLike labels are as follows:
Appending Different Arrays with the objects properties
var userName = [String]()
var userLikes = [String]()
func query(){
let commentsQuery = PFQuery(className: "UserStuff")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
for object in objects{
self.userName.append(object["userName"] as! String)
self.userLikes.append(object["userLikes"] as! String)
}
}
}
}
Ignore the fact that I don't have a .whereKey or any else statements to handle other cases... this is bare bones just for illustration of the question. Anyway, in this method, the userName and userLikes arrays are iterated through to populate the labels. The for object in objectss{} ensures that the indexes in one array (whether index 0,1,2,3,etc...) refers to/comes from the same object as the value in the index of the other array. However, I was wondering if would be better to do it as follows:
Appending the whole object to a PFObject array
var userObjects = [PFObject]()
func query(){
let commentsQuery = PFQuery(className: "UserStuff")
commentsQuery.findObjectsInBackground { (objectss, error) in
if let objects = objectss{
for object in objects{
self.userName.append(object)
}
}
}
}
With this method I could instead populate the labels with something like:
userNameLabel.text = String((userObjects[0])["userName"])
In this method all properties of the object would be accessible form the same array. I can see that this may have some advantages, but is this definitively the better way to do it/should I switch immediately?
I am going to say that the answer is that the latter of the two is probably the better method. This is because in the former, the information from a particular object is only linked between arrays by the order in the array. Any accidental or incorrectly scripted functions involving .append or .remove could skew the order between arrays and then an object's name might be the 3rd index in the nameArray but its likes may end up being the 4th index in the likesArray and it would be difficult to amend this issue. With the latter method, all information regarding an object's properties are linked to the object itself in the array and this issue is avoided.

How would you create a multidimensional array with n dimensions in Swift?

For instance, asume
var hierarchicalFileSystem: [[String]] = []
This allows one to support one layer of folders, but there appears to be no way to create an array in Swift like the one above but with an undefined number of nested String arrays.
Am I missing something here?
An array of arrays (of arrays of arrays...) of strings doesn't really make much sense to represent a file system.
What I'd instead recommend is making a class or struct to represent objects in the file system. Perhaps something like this:
struct FileSystemObject {
let name: String
let extension: String?
let isFolder: Bool
let contents: [FileSystemObject]?
}
Something like this let's us represent a file system quite nicely.
let fileSystem = [FileSystemObject]()
So, your fileSystem variable here is an array of FileSystemObjects and it represents the root. Each object within the root has its own set of details (its name, its file extension if it has one, and whether or not its a folder), and if it's a folder it has a non-nil contents property, and if it's a non-empty folder, that contents array of FileSystemObjects contains more file system objects (some of which are folders of course, which contain contents themselves).
What you can perhaps do is create an array with AnyObject and add new depths as you need it
var fileSystem: [AnyObject] = []
This would be a very bad way of representing a file system however and you should really go with some kind of tree structure like
struct Node {
children: [Node]?
parent: Node?
name: String
}
Swift is type-safe language. You have to declare type of your variable, or set it to AnyObject, but please don't. So, answering your question: yes it's possible:
var array: [AnyObject] = [[[1,2,3], [1,2,3]], [[1,2,3],[1,2,3]]]
But this is awful. Try to figure out better representation for your problem. Maybe custom structures.
you can have as much dimensional array as you want. is it a good idea? i don't think ...
var threeDArray: Array<Array<Array<String>>> = []
let oneDArray = ["1","2","3"]
let twoDArray1: Array<Array<String>> = [oneDArray, oneDArray, oneDArray, oneDArray, oneDArray]
let twoDArray2 = twoDArray1 + [["4","5","6"],["7","8","9"]]
threeDArray.append(twoDArray1)
threeDArray.append(twoDArray2)
let arr = [threeDArray,threeDArray,threeDArray]
print(arr.dynamicType) // Array<Array<Array<Array<String>>>>

Accessing instance properties inside of an array

I've imported several images into an actionScript 3 document. I've turned them all into symbols (movie clips) and given them instance names to reference from ActionScript.
Ok, so I'm putting the instances into an array so I can loop through them easily, but for some reason, whenever I'm putting in the instance name, I do a trace on the value in the array and it's giving me the symbol object back, rather than the instance object.
Basically trying to loop through the array to make each instance's visibility = false
Here's a sample:
var large_cap_extrusion_data: Array = new Array();
large_cap_extrusion_data[0] = large_cap_extrusion_menu_button;
large_cap_extrusion_data[1] = extrusion_border_large_cap
large_cap_extrusion_data[2] = "Large Cap";
large_cap_extrusion_data[3] = large_cap_main_menu_button;
var extrusion_data: Array = new Array();
extrusion_data[0] = large_cap_extrusion_data;
trace(extrusion_data[0][0]);
The traces gives:
[object large_cap_menu_button]
(the parent symbol)
rather than:
"large_cap_extrusion_menu_button"
I'd be very grateful if someone could tell me where I'm going wrong...
when you trace and object, by default it describes it type. What you want is the "name" property of the object.
Try this:
trace(extrusion_data[0][0].name);
that should give you the instance nema of the large_cap_menu_button rather than the class description. Either way, you have the right object I bet.

Resources