Swift model class error handling - ios

I have come with below approach for Model class initialisation with error handling. But whenever make init method as throwable in swift, I am not able to access from Obj C code. Declaration doesn't get create in projectname-Swift.h header file. Without "throws" it works perfect.
init?(dictionary: NSDictionary?)throws {
if let dictionary = dictionary {
// Parsing
}
else {
throw MyError.DictionaryNil("Nil Dictionary")
}
}
Am I missing anything here? My model class is subclass of NSObject and it has only one init method.

Well nullable & throwing isn't supported. You can remove ? and it will give you an error parameter.
Also you can't overload a throwing init as failable.
So it leaves you with the one with error parameter. Maybe you can call that from a convenience initializer in objective-c to send a nil error parameter.

If you'll want to use this with Objective C, I think you'll have to resort to the lowest common denominator in error handling: an NSError parameter.

Related

gomobile: binding callbacks for ObjC

I have a Go interface
type GetResponse interface { OnResult(json string) }
I have to subscribe on that event OnResult from ObjC using this interface.
func Subscribe( response GetResponse){ response.OnResult("some json") }
ObjC bind gives me a corresponding protocol and a basic class
#interface GetResponse : NSObject <goSeqRefInterface, GetResponse> {
}
#property(strong, readonly) id _ref;
- (instancetype)initWithRef:(id)ref;
- (void)onResult:(NSString*)json;
#end
So, I need to get this json in my ObjC env. How can I do that?
Subclassing If I subclass this GetResponse or just use it as is and pass to Subscribe routine, it crashes
'go_seq_go_to_refnum on objective-c objects is not permitted'
Category if I create struct on Go side with the protocol support, I can't subclass it but at least it's not crashes:
type GetResponseStruct struct{}
func (GetResponseStruct) OnResult(json string){log.Info("GO RESULT")}
func CreateGetResponse() *GetResponseStruct{ return &GetResponseStruct{}}
I have a solid object without obvious way to hook up my callback. If I make a category and override the onResult routine, it's not called. Just because overriding existing methods of class is not determined behavior according to AppleDoc. Anytime OnResult called from Go, the default implementation invokes and "GO RESULT" appears.
Swizzling I tried to use category and swizzle (replace method's implementation with mine renamed method) but it only works if I call onResult from ObjC env.
Any way to solve my issue? Or I just red the doc not very accurately? Please help me
I ran into a similar issue today. I thought it was a bug in gomobile but it was my misunderstanding of swift/objc after all. https://github.com/golang/go/issues/35003
The TLDR of that bug is that you must subclass the protocol, and not the interface. If you are using swift then you can subclass the GetResponseProtocol:
class MyResponse: GetResponseProtocol
If in objc, then you probably want to directly implement the GetResponse protocol, rather than subclassing the interface.

How to bridge Objective-C initWithError: method into Swift

I have a class defined in Objective-C, whose initializer is -initWithError: (the initializer can fail due to a dependence on an outside resource). I want this to bridge into Swift as init() throws. The regular initializer inherited from NSObject, -init, can be marked unavailable, as I don't want it to be used.
In Objective-C, I have:
#interface Foo : NSObject
- (instancetype _Nullable)initWithError:(NSError **)error;
#end
This works fine in Objective-C, of course.
In Swift, -initWithError gets bridged as init(error: ()) throws. This is presumably because removing withError: from the method name results in init() which conflicts with the inherited plain -init initializer. This can be called from Swift like so:
let foo = try Foo(error: ())
This is strange looking, as the error parameter is void. It would certainly be better if this were imported as init() throws. The obvious solution is to mark -init using NS_UNAVAILABLE in the Objective-C header. Unfortunately, this doesn't work. -initWithError: still gets bridged as init(error: ()), and trying to call try Foo() results in a compiler error saying that init() is unavailable in Swift.
Is there a more elegant solution to this so that try init() just works?
You can rename the function using NS_SWIFT_NAME. In this case:
- (instancetype _Nullable)initWithError:(NSError **)error NS_SWIFT_NAME(init());
That said, this feels like a compiler bug. I'd suggest opening a defect.

CGPDFDictionaryGetString function nil parameter exception

I found away of getting the types of content in a pdf. I built my code on this example where I am trying to read the content types from the dictionary. I found out that the key for type is Type. However I am getting an Error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is my code:
pdfDoc = CGPDFDocumentCreateWithURL(url)
if (pdfDoc != nil) {
let pdfCatalog=CGPDFDocumentGetCatalog(pdfDoc)
var uriStringRef: UnsafeMutablePointer< CGPDFStringRef>!
if (CGPDFDictionaryGetString(pdfCatalog, "Type", uriStringRef)) { //error
print(uriStringRef)
}
}
The only nil parameter is uriStringRef. However from the example, I am not supposed to set to any value before passing to CGPDFDictionaryGetString, and according to Apple's documentation, this should get the string returned value based on the key "Type". Am I right?
How can I solve this problem?
You are passing an uninitialized pointer to the CGPDFDictionaryGetString method, that is unsafe because you cannot know whether the method being called tries to read from a pointer before writing to it.
From the Swift blog post Interacting with C Pointers:
Pointers as In/Out Parameters
C and Objective-C don’t support multiple return values, so Cocoa APIs frequently use pointers as a way of passing additional data in and out of functions. Swift allows pointer parameters to be treated like inout parameters, so you can pass a reference to a var as a pointer argument by using the same & syntax.
For safety, Swift requires the variables to be initialized before being passed with &. This is because it cannot know whether the method being called tries to read from a pointer before writing to it.
So initialize the CGPDFStringRef 'pointer' like this:
var uriStringRef = CGPDFStringRef.init()
And pass it to the method using the inout syntax:
CGPDFDictionaryGetString(pdfCatalog, "Type", &uriStringRef)

'PFObject' does not have a member named 'subscript'

I understand, this particular error was already posted here and there and the code is somewhat basic, but I myself still unable to figure this one out & I need advice.
The thing is when I add the first two lines of code provided on parse.com for saving objects
var gameScore = PFObject(className:"GameScore")
gameScore["score"] = 1337
I get the following error for the second line:
'PFObject' does not have a member named 'subscript'
I'm on Xcode 6.3 beta 2.
All required libraries are linked with binary, <Parse/Parse.h> imported via BridgeHeader.
What syntax should I use?
This is happening due to the 1.6.4 version of the parse sdk which added Objective-C Nullability Annotations to the framework. In particular the file Parse/PFObject.h defines:
- (PF_NULLABLE_S id)objectForKeyedSubscript:(NSString *)key;
which is causing the Swift compile error. Removing the PF_NULLABLE_S fixes the problem.
On the other hand it seems correct that an object for a keyed subscript might be nil, so I suspect this is a Swift bug...
The problem seems to be the changed method signature, as kashif suggested. Swift doesn't seem to be able to bridge to the Objective-C method because the signature no longer matches the subscript method names.
Workaround 1
You can work around this without modifying the framework by calling the subscript method directly, instead of using the [] operator:
Instead of using the instruction below for getting the value of a particular key:
let str = user["key-name"] as? Bool
Please use the following instruction:
let str = user.objectForKey("key-name") as? Bool
and
Instead of using the instruction below for setting the value of a particular key:
user["key-name"] = "Bla bla"
Please use the following instruction:
user.setObject("Bla bla", forKey: "key-name")
Workaround 2
Another solution is to add an extension on PFObject that implements the subscript member and calls setValue:forKey::
extension PFObject {
subscript(index: String) -> AnyObject? {
get {
return self.valueForKey(index)
}
set(newValue) {
if let newValue: AnyObject = newValue {
self.setValue(newValue, forKey: index)
}
}
}
}
Note that this second workaround isn't entirely safe, since I'm not sure how Parse actually implements the subscript methods (maybe they do more than just calling setValue:forKey - it has worked in my simple test cases, so it seems like a valid workaround until this is fixed in Parse/Swift.
I've successfully run your exact code.
First, make sure you are indeed saving the object in the background, after you set the new value:
gameScore.save()
I would double check for misspellings in the class name and subclass; if they are incorrect, it will not work.
If that's not the problem, verify in Parse that the "score" subclass is set to be a number. If you accidentally set it to be a string, setting it as an integer will not work.
If these suggestions have not hit the solution, then I'm not sure what the problem is. Hope I helped.
I encountered a similar error with Parse 1.6.4 and 1.6.5 in the PFConstants.h header file with method parameters.
Xcode 6.3 beta 4 has a note in the "New in..." section regarding nullability operators.
Moving the nullability operator between the pointer operator and the variable name seems to comply/compile.
Changing:
PF_NULLABLE_S NSError *error
to:
NSError * PF_NULLABLE_S error
(i.e., NSError* __nullable error)
... resolved the compiler error.
This also worked for block parameters defined with id. So...
PF_NULLABLE_S id object
becomes:
id PF_NULLABLE_S object
In the above case, perhaps:
- (id PF_NULLABLE_S)objectForKeyedSubscript:(NSString *)key;
I.e., the nullability operator is after the pointer type.
I know its been a while but I encountered a similar problem when I was starting my first Parse application with SDK version 1.9.1.
The Quickstart guide had the code below as an example as to how to save values:
let testObject = PFObject(className: "TestObject")
testObject["foo"] = "bar"
testObject.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
println("Object has been saved.")
}
But, the line testObject["foo"] = "bar" returned the error 'PFObject' does not have a member named 'subscript'. I was able to work around the problem by changing the line to testObject.setValue("bar", forKey: "foo"). (As suggested by a tutorial video on YouTube: https://www.youtube.com/watch?v=Q6kTw_cK3zY)

In Objective C/Swift what do return within a method when an error is thrown?

Note: I say Objective C/Swift in the title of this post because I believe the answer to this question will apply to both languages, but the code I include in this post is Swift.
Java has a nice mechanism for Exception handling, but in Objective C we're either getting an error back from a delegate method or passing an error into a function by reference and then checking it afterwards. I'm wondering, what is the cleanest way to return from a method which expects a return value (such as an NSString, UIImage, or some other complex object) when an error is thrown? I don't know if "thrown" is the correct word to use in Objective C, but I really just mean when you make a call that returns a non-nil NSError.
For example, say I have this code which is trying to create a UIImage of a QR code:
func createQRImageWithString(string: String, andWidth width: Int32, andHeight height: Int32) -> UIImage {
var error : NSError?
let writer : ZXMultiFormatWriter = ZXMultiFormatWriter()
let result = writer.encode(string, format: kBarcodeFormatQRCode, width: width, height: height, error: &error)
if (error) {
println(error!.localizedDescription)
} else {
let image :CGImageRef = ZXImage(matrix: result).cgimage
return UIImage(CGImage: image)
}
}
This func does not compile because in the if statement no UIImage object is returned. The func creates a contract between the func and anyone who uses it, promising that a UIImage will be returned. In the event of an error, I have no UIImage to return. I guess I could make a dummy empty UIImage and return it, but that's certainly not what is expected. So what is the best way to handle this?
I gave an example using UIImage, but I'd like the answer to be generalizable to any situation where a method promises to return an object, but due to an unforeseen error returning that object is not going to go as planned.
In swift you can use the optional type and return a UIImage?, which allows you to either return a UIImage or nil.
The client will be statically enforced by the compiler to handle the optional return value, so this is a safe approach, as opposed to simply return nil (i.e. a NULL pointer) in Objective-C.
In Objective-C, throwing exceptions is reserved for programming errors (with very few exceptions). Swift doesn't have exceptions. The Swift designers seem to have decided that while having exceptions seemed to be a good idea, it actually isn't. (And there is plenty of Java code with atrocious exception handling).
If an error happens, your function should return a result that indicates that an error happened, and set an error variable to the description of the error. If your function calls another function that returns an error, then you need to decide whether the error of the function called indicates that your function returns an error or not. For example, a function checking whether WiFi is available or not may call a function that produces an error if there is no WiFi, in which case your function will return that there is no WiFi - without suggesting there is an error.
If the error in the called function means your function fails itself, then the same rules apply: Return a result that indicates there is an error, and set an error variable. In Objective-C, methods often return YES for success and NO for failure, or nil for failure and not-nil for success. In Swift, you usually return an optional value.
One answer is to return a UIImage? rather than a UIImage, returning nil if you have no image to return. Your calling code then has to determine what to do if you have no image. If it is possible that there is no image, then UIImage is probably not the right 'contract' to have with your calling code, whereas UIImage? is.

Resources