Objective-C to Swift framework throwing function conversion - ios

I am using CHCSVParser to parse some data. It has been installed through CocoaPods with use_frameworks!. Viewing the header file, there are two functions that I can use, I want to use the one that returns an error to see if something went wrong.
public convenience init!(contentsOfDelimitedURL fileURL: NSURL!, options: CHCSVParserOptions, delimiter: unichar)
// This is the one I want to use as I need the error if it fails.
public convenience init(contentsOfDelimitedURL fileURL: NSURL!, options: CHCSVParserOptions, delimiter: unichar, error: ()) throws
The actual Objective-C methods of the two above:
- (NSArray *)componentsSeparatedByDelimiter:(unichar)delimiter options:(CHCSVParserOptions)options;
- (NSArray *)componentsSeparatedByDelimiter:(unichar)delimiter options:(CHCSVParserOptions)options error:(NSError *__autoreleasing *)error;
I am using the error function like this:
do {
let components = try NSArray(contentsOfDelimitedURL: url, options: CHCSVParserOptions.RecognizesBackslashesAsEscapes, delimiter: 0x002C, error: ())
// Do what ever I need to do...
} catch {
print("Error: \(error)")
}
However I assume passing () to the error param is wrong. When I run this with something I know will fail the catch is never being called. Unsure on how to call this function correctly?
Using Xcode 7 beta 5.

You need to pass the error like this
var error: NSError?
var results = context.executeFetchRequest(request, error: &error)
and also i think you need to check the error object for any error rather than putting try catch

Related

completion handler's error in swift 3 and Xcode 8

I have working project in Xcode 7.3 with swift 2.2 version. Now I have updated Xcode 8 and migrated to swift 3. Now my project contains errors specially for blocks like success block of afnetworking.
Which gives error as
Cannot convert value of type '() -> ()' to expected argument type '((URLSessionDataTask, Any?) -> Void)?'
I don't understand how to solve this to work as per swift 3.
And there is also same like error in Facebook login.
Which gives error as
Cannot convert value of type '(FBSDKLoginManagerLoginResult!, NSError!) -> Void' to expected argument type 'FBSDKLoginManagerRequestTokenHandler!'
and
Cannot convert value of type '(_, _, NSError!) -> Void' to expected argument type 'FBSDKGraphRequestHandler!'
This all errors are related to handler blocks in swift 3. I don't understand the errors and so that can't able to solve. Any help will be appreciated. Thanks in advance.
For facebook - the problem is in new Swift rules about converting objective-c function parameters into Swift.
Previously, if parameters in objective-c code did not have nullability attributes(like nonnull or nullable), Swift converts it with ! making them non optional(forced unwrapping). Now it convert it with ? making them optional. That why you are getting an error. Before you were putting as a callback for login:
(FBSDKLoginManagerLoginResult!, NSError!) -> Void
Now you need to put:
(FBSDKLoginManagerLoginResult?, Error?) -> Void
Also, as you see, now you will not see NSError class. Instead of that Swift will put Error.This is also new rule. Now all "NS" prefixed in class names is removed in Swift(NSObject -> Object; NSError -> Error).
Example of working code for facebook login in Swift 3.0:
let manager = FBSDKLoginManager()
manager.logIn(withReadPermissions: ["public_profile"], from: self.controller) {
(loginResult: FBSDKLoginManagerLoginResult?, error: Error?) in
}
Example of working code for facebook request in Swift 3.0:
let request = FBSDKGraphRequest()
request.start {
(connection: FBSDKGraphRequestConnection?, result: Any?, error: Error?) in
}
As you see, now it is using Any type instead of objective-c id. In Swift 2.2 it was using AnyObject. It is also new Swift converting rule.
You do not need to specify callback parameters type. I did that in code for highlighting their real types. So you can just write code without them:
let manager = FBSDKLoginManager()
manager.logIn(withReadPermissions: ["public_profile"], from: self.controller) { (loginResult, error) in }
let request = FBSDKGraphRequest()
request.start { (connection, result, error) in }
But you need to remember that they are optional now.
In conclusion some converting rules that may affect you callback code:
Closure parameters are optional if in objective-c are not specified nullability attributes
All "NS" prefixes is removed for objective-c classes in Swift
If objective-c function had id parameter, in Swift 3.0 it will have type Any instead of AnyObject
Though I didn't know the error before that what Xcode want to inform me about the error, but I have removed type specification with object and it worked.
As
manager.post(methodname, parameters: param, progress: nil, success:{ (dataTask, responseObj) in
if let dict : NSDictionary = responseObj as? NSDictionary {
print("Response of \(methodname) : \(dict)")
if dict.object(forKey: "response") as? String == "success" {
CompletionHandler(true, dict)
} else {
CompletionHandler(false, dict)
}
}
})
Here with respect to question error is given at dataTask and responseObj which are with type specified. After removing type it worked fine.
Same as with facebook login
#IBAction func fbLoginClicked(_ sender: AnyObject) {
let app = UIApplication.shared.delegate as! AppDelegate
app.fbLoginManager = FBSDKLoginManager()
app.fbLoginManager.logOut()
app.fbLoginManager.loginBehavior = FBSDKLoginBehavior.native
app.fbLoginManager.logIn(withReadPermissions: ["email"], from: self, handler: { (result, error) -> Void in
if error != nil {
print(error?.localizedDescription)
} else {
if (result! as FBSDKLoginManagerLoginResult).isCancelled == true {
} else {
self.fetchFacebookUserDetail()
}
}
})
}
Here also I have removed type specification of result and error and problem solved. And followed this in whole app and it worked. I can run the project without error and also it is working. Thanks.

'Extra argument in call' when calling init! initializer on AVAssetReader swift

I see many versions of this question, but I've looked through many of them and haven't found something explaining my problem yet. I hope this isn't a repeat question.
I am simply trying to initialize an AVAssetWriter with this init! method defined in the documentation:
init!(URL outputURL: NSURL!,
fileType outputFileType: String!,
error outError: NSErrorPointer)
So I've written the following code in my playground:
var err : NSError? = nil
var outputPath = "\(NSTemporaryDirectory())mypath.mov"
var url = NSURL(fileURLWithPath: outputPath)
var fileManager = NSFileManager.defaultManager()
println("The putput path is \(outputPath)")
if(fileManager.fileExistsAtPath(outputPath))
{
fileManager.removeItemAtPath(outputPath, error: &err)
println(outputPath)
if(err != nil)
{
println("Error: \(err?.localizedDescription)")
}
}
var writeInitErr : NSError? = nil
var assetWriter = AVAssetWriter(URL: url, fileType: AVMediaTypeVideo, error: writeInitErr)
However, the last line throws the error "Extra argument 'URL' in call". None of the solutions I found in other questions about this error seem to apply here. Am I passing the wrong type to the parameter? Am I misunderstanding the use of the initializer?
Well, as is often the case, I figured out the answer minutes after asking the question.
The problem is actually with the "error: writeInitError" parameter, which should be
"error: &writeInitError"
Apparently the xcode error reporting is buggy, and was reporting a problem with the URL parameter instead. Fixing the error parameter solved the problem.
I suppose until the error reporting is improved, "Extra argument in call" translates to "Something is wrong with one of your parameters".

Swift - Calling initializer, missing argument

I am use Swift to call an initializer from objective-c class(JSONModel)
The class contains some initializer:
-(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err;
-(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err;
-(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
-(instancetype)initWithData:(NSData *)data error:(NSError **)error;
Since I want to call the initWithDictionary one, I write code like
var error:NSError
var loginRes = RegisterResponse(dictionary: dict , error: &error)
in which , RegisterResponse is a subclass of JSONModel, dict is a pre-used Dictionary variable
However, the complier complains like:
missing argument for parameter "usingEncoding" in call.
It seems that the complier think I am calling the
-(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err;
rather than the third one. Are there any solution?
-(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
is mapped to Swift as
init!(dictionary dict: [NSObject : AnyObject]!, error err: NSErrorPointer)
As explained in Adopting Cocoa Design Patterns / Error Reporting,
you have to pass an optional NSError as an in-out expression:
var error : NSError?
let loginRes = RegisterResponse(dictionary: dict, error: &error)
or with error checking:
var error : NSError?
if let loginRes = RegisterResponse(dictionary: dict, error: &error) {
println("success")
} else {
println(error!.localizedDescription)
}

Why do we use &error in Swift?

I commonly see a pointer to an optional error variable being used, just like in this block of code:
if fileManager.fileExistsAtPath(path)
{
var error: NSError?
if !fileManager.removeItemAtPath(path, error: &error)
{
println("Error removing the file : \(error)")
}
}
Why do we do this?
The error parameter is an inout parameter, and can set the value of error rather than returning it from the function. Look up "inout" in Apple's iBook on Swift.

SLRequestHandler error using Swift

I am trying to mimic the app as in this youtube tutorial, but using Swift and I am facing a problem constructing the closure as shown in the code snippet below.
func twitterTimeline() {
let account = ACAccountStore()
let accountType = account.accountTypeWithAccountTypeIdentifier(ACAccountTypeIdentifierTwitter)
// take action
account.requestAccessToAccountsWithType(accountType, options: nil,
completion: { (granted, error) in
if (granted) {
// invoke twitter API
let arrayOfAccount: NSArray = account.accountsWithAccountType(accountType)
if (arrayOfAccount.count > 0) {
let twitterAccount = arrayOfAccount.lastObject as ACAccount
let requestAPI = NSURL.URLWithString("http://api.twitter.com/1.1/atuses/user_timeline.json")
var parameters = Dictionary<String, String>()
parameters["100"] = "count"
parameters["1"] = "include_entities"
let posts = SLRequest(forServiceType: SLServiceTypeTwitter, requestMethod: SLRequestMethod.GET, URL: requestAPI, parameters: parameters)
posts.account = twitterAccount
// This is the Error Prone Area
let handler: SLRequestHandler = { (response, urlResponse, error) in
self.array = NSJSONSerialization.JSONObjectWithData(data: response, options: NSJSONReadingOptions.MutableLeaves, error: &error) as NSArray
}
posts.performRequestWithHandler(handler)
}
} else {
// do something
}
}
)
}
The error I get is
Cannot convert expression's type '($T1, $T2, $T3) -> $T0' to type '()'
I have tried checking and explicitly casting the types with no much help. I believe the error is somewhere else. Could anyone help me with what exactly is the trouble? I am sorry, if this turns out to be a näive question.
Thanks in advance,
Nikhil
This looks like an interesting case of error propagation in the compiler — I'd suggest filing a bug report with Apple.
The error message you're getting says that you can't assign a closure (which takes three parameters and returns one value) to something that takes no parameters. What's actually going wrong is that the handler closure you're defining takes its error input parameter and tries to pass it to JSONObjectWithData(_:options:error:). That's problematic from a language perspective because the error you're getting in is an immutable (optional) reference to one error, and the parameter you're passing it to expects a mutable pointer for it to (potentially) write another error into.
It's also incorrect API usage. The error you receive as a parameter in the closure is a report of an error that happened in whatever procedure calls your closure. You should log this error, present it to the user, or examine it so your app can gracefully fail. The error parameter you pass to JSONObjectWithData is a place for you to receive reports of additional errors that occur when decoding JSON from your data — you should be handling this error, too. These are two separate places to receive errors, so you shouldn't be passing one to the other.
If you fix that, you'll find a more helpful compiler message saying that the data: label on the first parameter to that function should be omitted. Also, you can use type inference for the options: parameter. So, your handler definition should look something more like this:
let handler: SLRequestHandler = { (response, urlResponse, error) in
// check for error and do something about it if need be, then...
var err: NSError?
if let jsonArray = NSJSONSerialization.JSONObjectWithData(response, options: NSJSONReadingOptions.MutableLeaves, error: &err) as? NSArray {
self.array = jsonArray
} else {
// do something about err
}
}
(You can also probably use a Swift typed array instead of an NSArray if you know what to expect from your JSON. But that's another subject for another question. Actually, several questions.)

Resources