I'm introducing CoreData to an existing class with objects that include to-Many-Relations.
Before I would have for example something like this:
"Player" is just a normal class with a result array...
for (y,result) in player.result!.enumerate() {
Now i would have to do it like this....
for (y,result) in (player.relResults!.allObjects as! [CDResult]).enumerate()
Is there a better way to do this?
Is there a way to avoid this ugly casting stuff?
An optional to-many relationship is represented in the NSManagedObject
subclass as an optional NSSet:
#NSManaged var results: NSSet?
So the type of the target entity of the relationship is lost here,
and some casting is needed. But you don't have to convert
the set to an array. You can cast it to a Set of the appropriate
type and iterate over the set:
for result in player.results! as! Set<CDResult> { ... }
Note that player.results can be nil, so this should be checked
before, or wrapped into an optional binding with if let or
guard let.
You could also change the property definition to
#NSManaged var results: Set<CDResult>?
and then simply iterate with
for result in player.results! { ... }
The disadvantage is that the change is lost if you re-generate the
managed object subclass in Xcode. Perhaps a future version of
Xcode will create Set<T> typed properties for to-many relationships.
(You may also have a look at "mogenerator" which is an alternative tool
to create the managed object subclasses with some nice additional features. It could be that mogenerator handles this better.)
Related
Is there a way in Swift 5 to iterate over all keypaths? I'm aware Mirror will give you string representations of the keys, but I can't use setValue as my objects are not inherited from NSObject and several values are not #objc compatible. I am attempting to do something like the below (pseudo coded):
func overwrite(source:T, overwrite:T) {
for keypath in overwrite.allKeypaths() {
source[keyPath: keyPath] = overwrite[keyPath: keyPath]
}
}
The end goal here is to create first a shallow copy into an existing object and possibly expand that out to include a deep copy based on the value type.
This is not possible in Swift. There is no way to get all keypaths on a given object. You can, however, use Mirror to look all properties of an object to get the string representation of objective-c objects. However, not all value types can be set using setValue in this way, as not all Swift types translate to objective-c.
I have arrays of simple structs that I would like to pack into a Core Data attribute. I don't need Core Data to handle each object independently, so packing into an array on one attribute seems fine and less overhead.
A complete solution might be to roll my own NSValueTransformer. I'd get full control over the process and storage. However, putting more work aside, since Swift 4 we now effectively have another approach to out-of-the-box serialization - storing objects as Data attributes, serialized by Swift 4's Codable protocol and (say) the JSONEncoder/JSONDecoder implementations. This would be an alternative to the more typical approach using a Transformable attribute serialized via the default NSValueTransformer under NSCoding.
An immediate advantage is not having to inherit from NSObject and conform to NSCoding, for what in my case are simple value types. This alone makes me want to start using Codable as my default approach. Clearly, using the JSON encoder would mean conversion and representation of data under utf8, even if Core Data/sqlite see this as Data/BLOBs. However from what I gather the default transformer to the (binary) plist format isn't exactly the most efficient, either.
Has anyone else taken this approach or otherwise know whether there may be any significant drawbacks or implications involved I’ve not considered? In short, could this be a reasonable approach?
I don't know if you still are looking for a solution to this issue, but here is an overview of a solution that I use. Assuming that aStruct: Codable, you can use this strategy.
In the core data model, I create a property such as encodedStructArray of type data for the desired entity.
Then in a swift extension for that entity, I create a computed read-write var with normal swift type:
var structArray: [aStruct] {
get {
guard let data = encodedStructArray else { return [] }
guard let result = try? JSONDecoder().decode([aStruct].self, from: data) else { return [] }
return result
}
set {
encodedStructArray = try? JSONEncoder().encode(newValue)
}
}
Now you can use the var just like you would anywhere but for performance reasons, pull it out and use that rather than using the var directly in multiple places since this example does not cache the value.
If you wish to cache the result, you would need to add a transient to the data model with the name structArray of undefined type and hold the cache value in its primitive. See the Core Data docs for an example.
I used to do things like below:
class A {
var param1:String?
var param2:[B]?
}
class B {
var param1:String?
var param2:String?
var param3:[C]?
}
class C {
var param1:String?
var param2:String?
}
But recently I found that dictionaries are more flexible. Class A can be replaced by the following dictionary.
[
"param1":"some string",
"param2":[
"param1":"some string",
"param2":"some string",
"param3":[
"param1":"some string",
"param2":"some string"
],
[
...
...
]
],
[
...
...
],
...
]
If we want to add "param3" into class C, we need to modify a lot of associated code if using class. But if we use dictionaries, we can just use "param3" as if it already exists.
A dictionary is just like a runtime defined class. I am wondering should we use dictionaries to replace data storing classes (i.e. models in MVC pattern) in all situations.
It depends on the use you have of your model. Making small classes enables you to give each class a specific additional behavior (for example more specific isolated accessory methods or helpers).
You can also test the model more easily by using only the piece you want and mock the other.
In general splitting responsibility is better because of maintenance and testability and clear code.
If your dictionary grows out of control then it is going to be very difficult for a newcomer on your team to use and understand the giant blob of data, rather than handling a lot of small objects with relationships between themselves.
If you add a new parameter you might need to change a lot of initializers.
That is normal I would say.
Also it depends on how you manage the model initialization. Maybe you use a factory that hides this complexity for you inside the rest of your code.
Or maybe you will need just to change it in your dependency injection root.
It clearly depends on the approach and scope of the object you are creating.
But in my opinion isolated objects are more reusable than a big blob of data in a dictionary
I agree that dictionaries are more extensible, but classes are safer.
One big unsafe thing about dictionaries is that you don't know whether a key exist or not at compile time. You have to put guard let or if let statements all over the place whenever you want to access something. If you don't do this, the app will crash at runtime when the key does not exist. Sure, you can fix it after it crashed, but you wasted a lot of time running your app and making that erroneous line of code to run and crash.
The other unsafe thing is type-unsafety. Since your dictionary contains different types of stuff, It must be a [String: Any]. Normally you can do this with classes:
someAObject.param2!.first!.param3!.first!.param1
If you use dictionaries you need:
(((dict["param2"]! as! [[String: Any]]).first!["param3"] as! [[String: Any]]).first! as! [String: Any])["param1"]
Just look at how much more code that is! Also, when you want a method to accept a parameter, you can write A or B or C if you are using classes and the method will only accept the type you specify. If you are using dictionaries, all you can write is [String: Any]. There is no compile time check whether that dictionary is of the acceptable type.
The third thing is about typos. If you typed a property name wrong, Xcode will tell you that even before you run the app. If you typed a dictionary key wrong, Xcode will not tell you that. You have to run that bit of code to know. Sure, you can put keys into constants, but that is very troublesome and the trouble definitely overweighs what you call "benefits" of dictionaries.
The fourth point is that dictionaries are value types. You might want some of the features of reference types.
And last but not least, you cannot add methods to dictionaries! A very important feature of classes is that they allow you to add methods and you can call them on instances of the class. If you made good use of this, you can write very readable code.
If we want to add "param3" into class C, we need to modify a lot of associated code if using class
Not if you designed your model well. I can't think of a reason why adding a new property to a class would require you to change lots of associated code.
I'm having a difficulty reconciling my admittedly incomplete understanding of optionals and this from the Swift 2.1 documentation:
Classes and structures must set all of their stored properties to an
appropriate initial value by the time an instance of that class or
structure is created. Stored properties cannot be left in an
indeterminate state.
I'd like to be able to do something like:
struct Name {
var firstName = "Flintstone First"
var middleName: String?
var lastName = "Flintstone"
}
var wilmaHusband: Name
wilmaHusband.firstName = "Fred"
where middleName may be nil. However, quite understandably according to that part of the Swift documentation, I encounter the error Struct wilmaHusband must be completely initialized before a member is stored...
Is what I'm trying to do--make a member potentially nil in a structure (or class) impossible in Swift? If so, it seems one seeming advantage of optionals--that they may hold the kind of data that may or may not be present in a structured object (such as a middle name in a name construct)--is lost.
I feel like I'm missing something fundamental here in my understanding of optionals, and I apologize in advance for my naïveté.
Since all members of the struct have an initial value
(including var middleName: String? which – as an optional – is
implicitly initialized to nil), you can create a variable of that
type simply with
var wilmaHusband = Name()
According to the Swift Programming Language reference, Dictionary instances are copied whenever they are passed to a function/method or assigned to a constant or variable. This seems inefficient. Is there a way to efficiently share the contents of a dictionary between two methods without copying?
It's true the documentation says that but there are also various notes saying it won't affect the performance. The copying will be performed lazily - only when needed.
The descriptions below refer to the “copying” of arrays, dictionaries, strings, and other values. Where copying is mentioned, the behavior you see in your code will always be as if a copy took place. However, Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so. Swift manages all value copying to ensure optimal performance, and you should not avoid assignment to try to preempt this optimization.
Source: Classes & Collections
Meaning - don't try to optimize before you actually encounter performance problems!
Also, don't forget that dictionaries are structures. When you pass them into a function, they are implicitly immutable, so no need for copying. To actually pass a mutable dictionary into a function, you can use an inout parameter and the dictionary won't be copied (passed by reference). The only case when a mutable dictionary passed as a parameter will be copied is when you declare the parameter as var.
You always have the option to define a custom, generic class with a Dictionary attribute:
class SharedDictionary<K, V> {
var dict : Dictionary<K, V>
// add the methods you need, including overloading operators
}
Instances of your SharedDictionary will be passed-by-reference (not copied).
I actually talked to someone on the Swift team today about "pass by reference" in Swift. Here is what I got:
As we all know, struct are pass by copy, classes are pass by
reference
I quote "It is extremely easy to wrap a struct in a class.
Pointing to GoZoner's answer.
Even though though a struct is copied, any classes defined in
the struct will still be passed by reference.
If you want to do traditional pass by reference on a struct, use
inout. However he specifically mentioned to "consider adding in
another return value instead of using inout" when saying this.
Since Dictionary defines KeyType and ValueType as generics:
struct Dictionary<KeyType : Hashable, ValueType>
I believe this means that if your KeyType and ValueType are class objects they will not be copied when the Dictionary itself is copied, and you shouldn't need to worry about it too much.
Also, the NSDictionary class is still available to use!
As other said "Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so." so performance should not be a big problem here. However you might still want to have a dictionary passed by reference for some other reasons. In that case you can create a custom class like below and use it just like you would use a normal dictionary object:
class SharedDictionary<K : Hashable, V> {
var dict : Dictionary<K, V> = Dictionary()
subscript(key : K) -> V? {
get {
return dict[key]
}
set(newValue) {
dict[key] = newValue
}
}
}
Trust the language designers: the compiler is usually smarter than you think in optimizing copies.
You can hack around this, but I don't frankly see a need before proving it's inefficient.