Error creating NSFetchRequest<T> in generic function - swift

I want a generic function to return a CoreData object:
func getManagedObject<T: NSManagedObject>(context: NSManagedObjectContext) -> T
{
let fetchRequest: NSFetchRequest<T> = T.fetchRequest() // ERROR
...
}
But I get Cannot assign value of type 'NSFetchRequest<NSFetchRequestResult>' to type 'NSFetchRequest<T>.
I've tried adding a where, but doesn't help. This only adds a warning Redundant conformance constraint 'T': 'NSFetchRequestResult':
func getEntity<T: NSManagedObject>(context: NSManagedObjectContext) -> T where T:NSFetchRequestResult
What does work is:
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = T.fetchRequest()
But why didn't my first attempt work?

Related

How does Swift ReferenceWritableKeyPath work with an Optional property?

Ground of Being: It will help, before reading, to know that you cannot assign a UIImage to an image view outlet's image property through the keypath \UIImageView.image. Here's the property:
#IBOutlet weak var iv: UIImageView!
Now, will this compile?
let im = UIImage()
let kp = \UIImageView.image
self.iv[keyPath:kp] = im // error
No!
Value of optional type 'UIImage?' must be unwrapped to a value of type 'UIImage'
Okay, now we're ready for the actual use case.
What I'm actually trying to understand is how the Combine framework .assign subscriber works behind the scenes. To experiment, I tried using my own Assign object. In my example, my publisher pipeline produces a UIImage object, and I assign it to the image property of a UIImageView property self.iv.
If we use the .assign method, this compiles and works:
URLSession.shared.dataTaskPublisher(for: url)
.map {$0.data}
.replaceError(with: Data())
.compactMap { UIImage(data:$0) }
.receive(on: DispatchQueue.main)
.assign(to: \.image, on: self.iv)
.store(in:&self.storage)
So, says I to myself, to see how this works, I'll remove the .assign and replace it with my own Assign object:
let pub = URLSession.shared.dataTaskPublisher(for: url)
.map {$0.data}
.replaceError(with: Data())
.compactMap { UIImage(data:$0) }
.receive(on: DispatchQueue.main)
let assign = Subscribers.Assign(object: self.iv, keyPath: \UIImageView.image)
pub.subscribe(assign) // error
// (and we will then wrap in AnyCancellable and store)
Blap! We can't do that, because UIImageView.image is an Optional UIImage, and my publisher produces a UIImage plain and simple.
I tried to work around this by unwrapping the Optional in the key path:
let assign = Subscribers.Assign(object: self.iv, keyPath: \UIImageView.image!)
pub.subscribe(assign)
Cool, that compiles. But it crashes at runtime, presumably because the image view's image is initially nil.
Now I can work around all of this just fine by adding a map to my pipeline that wraps the UIImage up in an Optional, so that all the types match correctly. But my question is, how does this really work? I mean, why don't I have to do that in the first code where I use .assign? Why am I able to specify the .image keypath there? There seems to be some trickery about how key paths work with Optional properties but I don't know what it is.
After some input from Martin R I realized that if we type pub explicitly as producing UIImage? we get the same effect as adding a map that wraps the UIImage in an Optional. So this compiles and works
let pub : AnyPublisher<UIImage?,Never> = URLSession.shared.dataTaskPublisher(for: url)
.map {$0.data}
.replaceError(with: Data())
.compactMap { UIImage(data:$0) }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
let assign = Subscribers.Assign(object: self.iv, keyPath: \UIImageView.image)
pub.subscribe(assign)
let any = AnyCancellable(assign)
any.store(in:&self.storage)
This still doesn't explain how the original .assign works. It appears that it is able to push the optionality of the type up the pipeline into the .receive operator. But I don't see how that is possible.
You (Matt) probably know at least some of this already, but here are some facts for other readers:
Swift infers types on one whole statement at a time, but not across statements.
Swift allows type inference to automatically promote an object of type T to type Optional<T>, if necessary to make the statement type-check.
Swift also allows type inference to automatically promote a closure of type (A) -> B to type (A) -> B?. In other words, this compiles:
let a: (Data) -> UIImage? = { UIImage(data: $0) }
let b: (Data) -> UIImage?? = a
This came as a surprise to me. I discovered it while investigating your problem.
Now let's consider the use of assign:
let p0 = Just(Data())
.compactMap { UIImage(data: $0) }
.receive(on: DispatchQueue.main)
.assign(to: \.image, on: self.iv)
Swift type-checks this entire statement simultaneously. Since \UIImageView.image's Value type is UIImage?, and self.iv's type is UIImageView!, Swift has to do two “automatic” things to make this statement type-check:
It has to promote the closure { UIImage(data: $0) } from type (Data) -> UIImage? to type (Data) -> UIImage?? so that compactMap can strip off one level of Optional and make the Output type be UIImage?.
It has to implicitly unwrap iv, because Optional<UIImageView> has no property named image, but UIImageView does.
These two actions let Swift type-check the statement successfully.
Now suppose we break it into three statements:
let p1 = Just(Data())
.compactMap { UIImage(data: $0) }
.receive(on: DispatchQueue.main)
let a1 = Subscribers.Assign(object: self.iv, keyPath: \.image)
p1.subscribe(a1)
Swift first type-checks the let p1 statement. It has no need to promote the closure type, so it can deduce an Output type of UIImage.
Then Swift type-checks the let a1 statement. It must implicitly unwrap iv, but there's no need for any Optional promotion. It deduces the Input type as UIImage? because that is the Value type of the key path.
Finally, Swift tries to type-check the subscribe statement. The Output type of p1 is UIImage, and the Input type of a1 is UIImage?. These are different, so Swift cannot type-check the statement successfully. Swift does not support Optional promotion of generic type parameters like Input and Output. So this doesn't compile.
We can make this type-check by forcing the Output type of p1 to be UIImage?:
let p1: AnyPublisher<UIImage?, Never> = Just(Data())
.compactMap { UIImage(data: $0) }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
let a1 = Subscribers.Assign(object: self.iv, keyPath: \.image)
p1.subscribe(a1)
Here, we force Swift to promote the closure type. I used eraseToAnyPublisher because otherwise p1's type is too ugly to spell out.
Since Subscribers.Assign.init is public, we can also use it directly to make Swift infer all the types:
let p2 = Just(Data())
.compactMap { UIImage(data: $0) }
.receive(on: DispatchQueue.main)
.subscribe(Subscribers.Assign(object: self.iv, keyPath: \.image))
Swift type-checks this successfully. It is essentially the same as the statement that used .assign earlier. Note that it infers type () for p2 because that's what .subscribe returns here.
Now, back to your keypath-based assignment:
class Thing {
var iv: UIImageView! = UIImageView()
func test() {
let im = UIImage()
let kp = \UIImageView.image
self.iv[keyPath: kp] = im
}
}
This doesn't compile, with the error value of optional type 'UIImage?' must be unwrapped to a value of type 'UIImage'. I don't know why Swift can't compile this. It compiles if we explicitly convert im to UIImage?:
class Thing {
var iv: UIImageView! = UIImageView()
func test() {
let im = UIImage()
let kp = \UIImageView.image
self.iv[keyPath: kp] = .some(im)
}
}
It also compiles if we change the type of iv to UIImageView? and optionalize the assignment:
class Thing {
var iv: UIImageView? = UIImageView()
func test() {
let im = UIImage()
let kp = \UIImageView.image
self.iv?[keyPath: kp] = im
}
}
But it does not compile if we just force-unwrap the implicitly-unwrapped optional:
class Thing {
var iv: UIImageView! = UIImageView()
func test() {
let im = UIImage()
let kp = \UIImageView.image
self.iv![keyPath: kp] = im
}
}
And it does not compile if we just optionalize the assignment:
class Thing {
var iv: UIImageView! = UIImageView()
func test() {
let im = UIImage()
let kp = \UIImageView.image
self.iv?[keyPath: kp] = im
}
}
I think this might be a bug in the compiler.

Generic core data fetch request

I am trying to create sort of a generic wrapper for simple core data fetches.
What I wanted to achieve, is, instead of writing multiple redundant methods that look like this:
func loadNSMOSubclass() -> [NSMOSubclass] {
let fetchRequest: NSFetchRequest<NSMOSubclass> = NSMOSubclass.fetchRequest()
do {
let result = try mainContext.fetch(fetchRequest)
return result
}
catch {
return []
}
}
I thought I could create a generic helper for that:
struct EntityLoader<T> where T: NSManagedObject {
func loadEntity() -> [T] {
let fetchRequest: NSFetchRequest<T> = T.fetchRequest()
do {
let mainContext = CoreDataState().mainContext
let result = try mainContext.fetch(fetchRequest)
return result
}
catch {
return []
}
}
}
However, at that point the compiler has a weird error:
Cannot convert value of type NSFetchRequest<NSFetchRequestResult> to specified type NSFetchRequest<T>
where the suggested solution is even stranger, since everything compiles when I do a casting:
let fetchRequest: NSFetchRequest<T> = T.fetchRequest() as! NSFetchRequest<T>
That might be ugly, but I could live with that. However when I run this code I am getting a fatal error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'executeFetchRequest:error:
<null> is not a valid NSFetchRequest.'
Am I doing something wrong, or are these just some Swift's constraints that are currently impossible to surpass?
Problem here is fetchRequest() is a auto-generated helper function and it's only available for your NSManagedObject sub classes. It's not available for your generic type. Therefore, instead of using that function you have to use it's implementation with your generic type like this,
replace this line,
let fetchRequest: NSFetchRequest<T> = T.fetchRequest()
with this,
let fetchRequest: NSFetchRequest<T> = NSFetchRequest<T>(entityName: String(describing: T.self))

Class casting dynamically in swift

I am trying to dyanmically cast to a class in Swift. Is this possible? Here is the code I am trying to use:
let stringClass: AnyClass = NSString.self
let anyObject: AnyObject = "foo"
let string = anyObject as! stringClass
The code fails to compile at the cast. Is this possible and if so, why is the right syntax?
Real use case
Here is the real issue. I am attempting to refactor this code:
switch (value) {
case "valueOne":
viewController = storyboard.instantiateViewController(withIdentifier: "foo") as! FirstViewController
case "valueTwo":
viewController = storyboard.instantiateViewController(withIdentifier: "bar") as! SecondViewController
default:
return nil
}
into:
let controllersDictionary: [String: (String, UIViewController.Type)] = [
"valueOne" : ("bar", FirstViewController.self),
"valueTwo" : ("foo", SecondViewController.self)
]
let tuple = controllersDictionary[value]!
let identifier = tuple.0
let cast = tuple.1
let viewController = storyboard.instantiateViewController(withIdentifier: identifier) as! cast
I'm not sure exactly what you're trying to achieve, but here's a working version of your example:
func cast<T>(value: Any, to type: T) -> T? {
return castedValue as? T
}
let inputValue: Any = "this is a test"
let inputType = String.self()
let casted = cast(value: inputValue, to: inputType)
print(casted)
I'm not seeing what the cast at this point is for. You can write:
let controllersDictionary: [String: String] = [
"valueOne" : "bar",
"valueTwo" : "foo"
]
let identifier = controllersDictionary[value]!
let viewController = storyboard.instantiateViewController(withIdentifier: identifier)
The cast does nothing for you in the code that you have shown. viewController is typed as UIViewController, but it is the correct underlying view controller subclass thanks to polymorphism; whatever the class is in the storyboard, that's the class of this instance.
The only time you need to cast down is when you have to message an instance with a message belonging only to the subclass, and you have not shown any such need at this point in your code.
While there are/will be ways to make this kind of thing work, the Swifty solution (IMO) is to have your desired classes adhere to a protocol that defines the shared behavior you're trying to use, or simply use a super class they have in common
This allows the dynamism requried (in most cases at least) while still allowing the compile-time checks that prevent run time errors.
For your example,
protocol Stringable {
func toString() -> String
}
extension String: Stringable {
func toString() -> String {
return self
}
}
let thing = "foo"
let anything: Any = thing
let test: String? = (anything as? Stringable)?.toString()
Note that this requires "Any" rather than "AnyObject" since you need to cast to a protocol
Since you mentioned ViewControllers, I thought this might help:
static func createViewController<T: UIViewController>(storyboard: String, scene: String) -> T? {
return UIStoryboard(name: storyboard, bundle: nil).instantiateViewControllerWithIdentifier(scene) as? T
}
The x as! Y.Type syntax only works when Y is explicitly stated.
So, the compiler wants x as! NSString. The compiler crash is a bug, I suggest that you file a report.
And just think about it for a second, how would that even help you? stringClass is of type AnyClass, and you're force casting an AnyObject which already conforms to AnyClass. You should cast when you have a static type in mind, because casting doesn't really make any sense otherwise.
If you want to check, however, whether your object's class is a subclass of a particular type, you'd use:
x is Y.Type
Its possible so long as you can provide "a hint" to the compiler about the type of... T. So in the example below one must use : String?.
func cast<T>(_ value: Any) -> T? {
return value as? T
}
let inputValue: Any = "this is a test"
let casted: String? = cast(inputValue)
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
Why Swift doesn't just allow us to let casted = cast<String>(inputValue) I'll never know.
One annoying scenerio is when your func has no return value. Then its not always straightford to provide the necessary "hint". Lets look at this example...
func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
completion(value as? T)
}
The following client code DOES NOT COMPILE. It gives a "Generic parameter 'T' could not be inferred" error.
let inputValue: Any = "this is a test"
asyncCast(inputValue) { casted in
print(casted)
print(type(of: casted))
}
But you can solve this by providing a "hint" to compiler as follows:
asyncCast(inputValue) { (casted: String?) in
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
}

Swift2: return a optional type object

I'm new in the swift2 world and I currently struggle with a simple function :
// Get all moves for the first category
func getMuscles() -> BodyPart {
let bpart:BodyPart?
if let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext {
do{
let fetchRequest = NSFetchRequest(entityName: "BodyPart")
let fetchResults = try managedObjectContext.executeFetchRequest(fetchRequest) as! [BodyPart]
bpart = fetchResults[0]
} catch let error as NSError {
print(error)
bpart = nil
}
}
return bpart
}
How can I solve this issue ? And what are the 'best-practices' in swift2 for defining a function ?
Thank you
EDIT 1
I've tried to change the signature of the method, but error is still here :
The question you should be asking yourself is whether getMuscles() must always return an object or if it's fine for it to return a nil.
By changing the method signature to func getMuscles() -> BodyPart?,
you're basically stating that a nil might be returned from that method,
thus solving your immediate compile time issue.
In that particular context, because you're fetching objects from CoreData,
it might be wise to allow getMuscles() to return a nil.
The way you define your functions (if they return optionals ?, or not) entirely depends on the calling code.
Change your method signature to :
func getMuscles() -> BodyPart?
But be careful while unwrapping the return value when the this function is being called.
Just return:
func getMuscles() -> BodyPart? { }
Thats nothing to do with SWIFT2.. The return type is expecting some value BodyPart not an optional value BodyPart?...But you are returning a optional value bpart
func getMuscles() -> BodyPart {
let bpart:BodyPart?
....
return bpart
}
If you want to return bpart as it is you need to create the return type as optional
func getMuscles() -> BodyPart? {
let bpart:BodyPart?
....
return bpart
}
or if you want to just return the value try this
func getMuscles() -> BodyPart {
let bpart:BodyPart = ()//initialize here dont make optional
....
return bpart
}

Subclass AFNetworking JSON Response Serialization to insert JSON response body in to error data in Swift 2

AFNetworking does not return the JSON response body when it fails (e.g. 400 status code), therefore you must subclass AFJSONResponseSerializer and fill in the error object with such. This is the recommended here and an example is here
Up until Swift 2, I used the following code to achieve such:
import Foundation
let JSONResponseSerializerWithDataKey : String = "JSONResponseSerializerWithDataKey"
let JSONResponseUndefinedAPIFailureReason : String = "UNKNOWN_ERROR"
class JSONResponseSerializerWithData: AFJSONResponseSerializer
{
override func responseObjectForResponse (response: NSURLResponse, data: NSData, error: AutoreleasingUnsafeMutablePointer
<NSError?>) -> AnyObject
{
var json : AnyObject = super.responseObjectForResponse(response, data: data, error: error) as AnyObject
if (error.memory? != nil)
{
var errorValue = error.memory!
var userInfo = errorValue.userInfo
if let errorDetail = json["detail"] as? String
{
userInfo![JSONResponseSerializerWithDataKey] = errorDetail
}
else
{
userInfo![JSONResponseSerializerWithDataKey] = JSONResponseUndefinedAPIFailureReason
}
error.memory = NSError(domain: errorValue.domain, code: errorValue.code, userInfo: userInfo)
}
return json
}
}
Start with Swift 2, a new type of Error handling was introduced.
The signature of the above function is now:
override func responseObjectForResponse(response: NSURLResponse!, data: NSData!) throws -> AnyObject
I am having trouble achieving the same as above inside a do-catch statement as it seems the failure does not invoke the catch statement, and thus there is no access to the error object. Further, new ErrorTypes are essentially empty and don't contain
This is what I've tried, but the catch statement is never called:
class JSONResponseSerializerWithData: AFJSONResponseSerializer
{
override func responseObjectForResponse(response: NSURLResponse!, data: NSData!) throws -> AnyObject
{
do
{
return try super.responseObjectForResponse(response, data: data)
}
catch
{
let nsError = (error as NSError)
var userInfo = nsError.userInfo
if let errorDetail = userInfo["detail"] as? String
{
userInfo[JSONResponseSerializerWithDataKey] = errorDetail
}
else
{
userInfo[JSONResponseSerializerWithDataKey] = JSONResponseUndefinedAPIFailureReason
}
throw NSError(domain: nsError.domain, code: nsError.code, userInfo: userInfo)
}
}
}
I've tried stepping through the AFNetworking2 Library an the body of the response is there, so I could sub-class it in Objective-C rather than Swift, but would prefer doing such if possible.
Am I handling this incorrectly with a do-catch statement? Any pointers in the right direction would be greatly appreciated.
After more digging, I have found that the issue is as described here.
The error is not thrown, only filled, therefore catch is never called. To induce this old behaviour you can add "NS_SWIFT_NOTHROW" to the end of the Objective-C signature in the header file as described here
This will change the signature to:
override func responseObjectForResponse(response: NSURLResponse?, data: NSData?, error: NSErrorPointer) -> AnyObject?
Which can then be used like before.