How do you know when to cast an item in swift? - swift

I see it done all the time but don't know how other coders know when to cast an item. Here is an example that recently had me wondering how the coder knew to cast the item:
let item = NSEntityDescription.insertNewObjectForEntityForName("Item", inManagedObjectContext: ad.managedObjectContext) as! Item

The insertNewObject(forEntityName:into:) API that you're describing returns a NSManagedObject, which is the way new managed objects are created, configured and returned to you to make use of.
In CoreData, all NSManagedObjects saved are actually subclasses of the NSManagedObject base class, so if you want to let item = from that call, you'll need to cast it to the actual subclass type it's supposed to be.
Makes sense?

Related

When deleting a CoreData Object, how to also delete all of its related Objects in Swift5?

I am currently working with CoreData.
Problem: I have a CoreData Entity User with a one-to-many-relationship to another CoreData Entity Badges. I am now trying to delete a User and, obviously, also would like to delete all of his Badges.
Deleting the User itself is pretty straight forward:
context.delete(selectedUser)
However I have to first delete all of the User's Badges. This is the complicated Part for me:
for badge in selectedUser.badges {
context.delete(badge)
}
When doing so, this Error occurs: Cannot convert value of type 'NSSet.Element' (aka 'Any') to expected argument type 'NSManagedObject'
My Possible Solution: I was thinking of simple downcasting: context.delete(badge as! NSManagedObject). However I am not sure whether this is possible.
Question: What is the best Practice for achieving the Goal I described above? Or is there maybe a CoreData way to recursively delete all related Objects?
Thanks for your help.
selectedUser.badges is an NSSet of Badges, therefore you can cast its elements to Badge or to NSManagedObject:
for badge in selectedUser.badges {
context.delete(badge as! NSManagedObject)
}
You can also cast the NSSet to its Swift counterpart Set:
for badge in selectedUser.badges as! Set<Badge> {
context.delete(badge)
}
But in order to delete all related objects if a user is deleted, the simple solution is to set the “Deletion Rule” for the relationship to “Cascade”.

When recasting in Swift, when do you use "<object> as <type>" and when to use <type>(<object>)

For example this code works:
var i = 5
var str: String
str = String(i)
but this does not:
str = i as String
On the other hand this code works.
let controller : MyViewController
controller = storyboard?.instantiateViewController(withIdentifier: "MyViewController") as! MyViewController
but this does not:
controller = MyViewController(storyboard?.instantiateViewController(withIdentifier: "MyViewController"))
String(i) is used to create a String object from another value (Int in this case`). This is the proper way to convert from one type to another. This is not a cast.
i as String is a cast, not a conversion. Casting does not alter the actual object's type. Since i is an Int, you can't simply cast it to a String. Casting is typically done when you get a reference to an object using a base class or maybe Any and you wish to have a variable that specifies the actual data type of the object.
In your second example you declared a variable with a type of myViewController. Presumably this is a subclass of UIViewController. The use of as! myViewController works because you setup your storyboard indicating that the view controller with the identifier of "myViewController" is really of type myViewController. The call to instantiateViewController(withIdentifier:) has a return type of UIViewController. So the cast (as! myViewController) allows you to indicate that the UIViewController really is a myViewController. It fails without the cast because you can't assign a value of type UIViewController to a variable of type myViewController (but you could do the reverse).
The title of the question asks:
when do you use “<object> as <type>” and when to use <type>(<object>)
These two patterns are designed to solve very different data type issues:
We use the former syntax when we have an object that is of some type, but where you happen to know that it is (or want to test whether it is) really an instance of some subclass of that type and you need/want to access some property/method unique to that subclass.
In your instantiateViewController example, that method is defined to return UIViewController. The instantiateViewController method has no way of knowing whether your storyboard scene may have specified some UIViewController subclass as the "base class" for that scene, so it returns a UIViewController reference. But you happen to know that the scene in question actually returns some custom subclass of UIViewController, so you can downcast as your MyViewController subclass. You'd generally do that if you need to interact with some property/method unique to your UIViewController subclass.
Note, nowadays, you don't use as for downcasting. You'd either use as! to do a forced downcast if you know with 100% certainty that the downcast will succeed. Or you'd use as? as a safe way to test if the cast succeeded (e.g. with an if let or guard let construct).
We use the latter syntax when you want to create an entirely new instance of some other type.
Consider:
let string = String(i)
This actually is creating a new String instance, using i during its initialization.
This wouldn't make sense in the instantiateViewController example, because we don't want to create a new MyViewController instance from what that method returned. Rather we merely want to tell the compiler "yes, I know that method will return UIViewController (or subclass) instance, but I happen to know it's really MyViewController instance." So, in that case, we use the as! (or as?) downcast.
As Martin R points out, there are other types of casts (bridging casts, casts of Any and AnyObject types, etc.). But that's not relevant for your particular example.
For a general discussion of casting, see Type Casting section of The Swift Programming Language. See Using Swift with Cocoa and Objective-C for discussion of bridging types, id casts, etc.

Swift - Empty NSMutableDictionary or NSDictionary? Optional

Just curious, in Swift, is it more ideal to initialize an empty NSMutableDictionary variable, NSMutableDictionary = [:], and later re-assign its value to a new dictionary (coming from an API for example),
OR, is it better to declare an optional NSDictionary, NSDictionary? and assign it to a new dictionary?
So with Swift it would technically be best practice to use a Dictionary type. Like this for example:
var dict: Dictionary<String, Int>
If you need the dictionary as a whole to be able to be nil use an optional.
This depends on your needs, do you want it to be nil sometimes? is it nil sometimes?
If an array is always gonna have value, even if it's an empty value, I personally like to Initialize it right away, and not hassle with unwrapping everywhere.
Maybe if you had two arrays, one was normal array, and the second one was a searched result. You might wanna check if searched result is nil first, if it is, show the array1, if it isn't show it instead.
And this is implying you only search "sometimes", thus that array is only sometimes used - so you might as well have that deallocated when not in use, if you aren't using it most of the time.
EDIT: I've been using arrays in my example, but same applies for a dictionary in those situations.
EDIT: In Swift It's best to avoid 'NS' classes, sometimes you have to use them, sure. But Swift's Dictionary does the job.
Example:
var sometimesUselessDict: Dictionary<String, AnyObject>?
var alwaysUsedDictionary = Dictionary<String, AnyObject>()
Cheers
You should make it optional only if you need to be able to distinguish a dictionary that's empty from one that doesn't exist at all. For instance, if you're receiving data from a server, you might want to distinguish between a successful response that returned no data (empty dictionary) and a failed or invalid response (nil).
If that distinction isn't important, I would always go with a non-optional to avoid unnecessary unwrapping.

argument for generic parameter could not be inferred

I'm trying to save an array with NSUserDefaults and then load the array, but I get the error "argument for generic parameter could not be inferred." Is there anything I am doing wrong? No one seems to be having this problem in swift, so I can't find any solutions.
IBAction func loadData(sender: AnyObject) {
if let testCompositeArray = defaults.objectForKey("testScoreSATArray") as? Array {
self.showDataLabel.text = defaults.objectForKey("testScoreSATArray") as Array
}
}
The reason you received your original error is that in Swift, Array is a generic container that holds values of a specific type. So you can have an Array<Int> that holds integers, or an Array<String> that holds strings. But you can’t have just an Array. The type of the thing the array contains is the generic parameter, and Swift is complaining because it can’t figure out what that type should be. Sometimes it can infer that type from the context of the code around it, but not always, as in this case.
You can resolve the problem by giving the type of the thing you are storing:
IBAction func loadData(sender: AnyObject) {
if let testCompositeArray = defaults.objectForKey("testScoreSATArray") as? Array<Int> {
self.showDataLabel.text = toString(testCompositeArray)
}
}
Instead of writing Array<Int>, you can write the shorter form, [Int]
You can also solve the problem by using NSArray, as you’ve found. Unlike Array, NSArray doesn’t use generics, since it originates in Objective-C which has a different approach to Swift. Instead, NSArray holds only one kind of thing, an AnyObject. This is is a reference that can point to instances of any class.
However, there’s a big downside to using NSArray and AnyObject, which is that every time you use a value they contain, you often have to “cast” the value to a real thing, like an integer or a string. This can be a pain, and worse, sometimes can cause errors when you assume you have one kind of thing when actually you have another. Swift generally encourages you to be more specific about types to avoid errors like this.

NSUserDefaults and Conditional Encoding of Custom Objects in an NSArray

I know there are a lot of posts out there concerning the problem of how to archive custom objects in an NSArray or NSMutableArray and save them in NSUserDefaults. Conforming to the NSCoding Protocol and saving to NSUserDefaults isn't problematic and I use NSUserDefaults quite a lot to store the user-submitted data in my app - it mostly contains objects representing a Person (let's call the NSObject subclass "Person") which can have multiple objects of the NSObject subclass "Property" stored in an NSMutableArray. Therefore, the data structure looks like this:
NSMutableArray "persons":
Person "aPerson":
NSMutableArray "properties":
Property "aProperty"
Property "anotherProperty"
Person "anotherPerson:
...
Archiving and restoring the information was not problematic at first, because both Person and Property conform to the NSCoding Protocol - but now a problem occured which I was not able to solve yet despite those thousands of google requests in the last couple days ;)
Some of the Property objects contain references to other Persons ("Participants", which are linked to the same property and are contained in an NSMutableArray).
When I store the whole data to NSUserDefaults using NSKeyedArchiver, I use
[aCoder encodeObject:participants forKey:#"participants"];
in the Property's "encodeWithCoder" method to archive the NSMutableArray "participants" which stores the references to other Person objects. But when I decode those Person objects, they are created new and separated from the Person objects that already exist somewhere else. The NSMutableArray "participants" only contains references, weak links to the Person objects and should therefore conditional encode its content, as one can do with other objects manually in "encodeWithCoder":
[aCoder encodeConditionalObject:anObject forKey:aKey];
When the NSMutableArray gets decoded, it should represent a list of references to already existing Person objects - not completely new ones! The test "aPerson==[[aDecoder decodeObjectForKey:#"participants"] objectAtIndex:0]" is currently returning NO although it had returned YES before the encoding/decoding process has taken place.
I hope my explanation is somehow understandable and you can help me with my problem :) In simple words: How can I conditional encode custom objects contained in an NSMutableArray?
Thank You !
If NSMutableArray would use encodeConditionalObject:forKey: for the objects it contains, it would just mean that those objects aren't encoded at all, if they're not encoded unconditionally somewhere else in your object graph. This wouldn't help you in this case (the array would just be empty).
The problem is that you cannot really encode references to objects in memory. An object reference is basically just a pointer to an address in memory. When you start your app the next time and create the very same object (whether by unarchiving or otherwise), it will almost definitely have a different address in memory. There is no way the unarchiver can 'magically' know, which existing object corresponds to the reference it has archived, because the memory address (the object's 'identity') loses all its meaning when you quit your app.
You have to use other means of identifying your objects, such as database row IDs, dictionary keys, etc. and establish the connection of the archived key and the existing object corresponding to that key manually.
I had an issue with this too. I have objects that have an array of weak links to other objects. I know all the objects linked to will be encoded, so I just want to make sure I can rebuild the links.
With a single weak link is it possible to use:
aCoder.encodeConditionalObject(thing, forKey: "Thing")
...and if that item has already been encoded from elsewhere, then a reference to that encoded item will be used.
But, what to do if you have an array full of 'conditional' items, where the array needs to be encoded unconditionally?
I ended up wrapping the items I want to link to.
class thingLink: NSObject, NSCoding
{
weak var thing: Thing?
init(_ thing: Thing) {
self.thing = thing
}
required init?(coder aDecoder: NSCoder) {
thing = aDecoder.decodeObject(forKey: "Thing") as? Thing
}
func encode(with aCoder: NSCoder) {
// We encode these conditionally as they must be used elsewhere
aCoder.encodeConditionalObject(thing, forKey: "Thing")
}
}
...then I store these in my array which I encode as usual.
aCoder.encode(things, forKey: "Things")
If I move to a database to store things, I think this will help there too, because I will need a separate table to store the links and maintain priority etc.