Changes in Swift 1.2 that confusing me - swift

I updated Xcode to 6.3, and found there’s some new error in my codes with new Swift 1.2.
user.signUpInBackgroundWithBlock {
(success:Bool!, error:NSError!) -> Void in
if !(error != nil) {
println("sign up successfully")
var loginAlert: UIAlertController = UIAlertController(title: "Sign Up", message: "Sign Up Succeeded", preferredStyle: UIAlertControllerStyle.Alert)
self.presentViewController(loginAlert, animated: true, completion: nil)
loginAlert.addAction(UIAlertAction(title: "Okay", style:
I got this error:
Cannot invoke signUpInBackgroundWithBlock with an argument list of type ((Bool!, NSError!) -> void)
How can I fix it?
Another one
#IBAction func endend(sender: AnyObject) {
if (PFUser.currentUser() == nil) {
PFUser.logInWithUsernameInBackground(usernameTextField.text, password: passwordTextField.text){
(user:PFUser!, error:NSError!) -> Void in
if user != nil {
println("login chenggong")
var tlvc = TimelineViewControllerTableViewController()
self.presentViewController(tlvc, animated: true, completion: nil)
}
else {
println("failed")
}
}
}
}
I got this error :
“UITextField” does not have member named “text”.
And I got 3 errors that about }, it says
Expected “,” separator.
Expected expression in list of expressions.
Expected “)” in expressions.
I can ran my app before Swift 1.2, but now...

Following code worked for me:
PFUser.logInWithUsernameInBackground(username.text as String!, password: password.text as String!){
(loggedInuser: PFUser?, signupError: NSError?) -> Void in

In Xcode, go to Edit > Convert... > To Latest Swift Syntax...
There are several language syntax changes in the new release, so Apple included a tool to help migrate older Swift code. From what I've been reading it's largely useful but doesn't always solve 100% of the problems. Hopefully it'll at least reduce the number of errors you're seeing.

For Parse sign up blocks, the Swift 1.2 compiler does not like it when you force unwrap the boolean success parameter.
Removing the '!' after 'success:Bool' should remove the errors you are getting.
Try changing:
user.signUpInBackgroundWithBlock{(success:Bool!, error:NSError!) -> Void in
To:
user.signUpInBackgroundWithBlock{(success:Bool, error:NSError!) -> Void in

Try changing:
user.signUpInBackgroundWithBlock{(success:Bool!, error:NSError!) -> Void in
To:
user.signUpInBackgroundWithBlock{(success:Bool?, error:NSError?) -> Void in

Related

swift 3 from working swift 2.3 : #escaping with optionals and calls needing error chain management

I have optionals (NSError?) that are flagged by Xcode/Swift as non-escaping. These are called within a function (that has #escaping on its closure) by a second function (also with #escaping on its closure). The problem seems to be that the errors are not captured in the closure of either function and so Xcode/Swift is seeing them as potentially escaping.
Previous stack overflow posts had noted 'withoutActuallyEscaping' as a workaround. That no longer seems to be supported (Xcode version 8.2.1).
Refactoring to try to keep everything local to the function has not worked. I've also tried moving all of the NSError? to Error (via local enum), but that also hasn't worked.
The core problem is how to enable an error returned (via success/failure) from one function to the calling statement to be either captured locally and retained or tagged as escaping so that Swift/XCode can compile.
Again, this was working in Swift 2.3, so I'm looking for suggestions on how to refactor or what to look at for correctly handling these calls with NSError or Error.
This is part of a large code stack (about 25K lines). I'm posting part of the stack below (given Hamish's comment) to try and make the question clearer. Currently there are about 17-different variations of this error that are present in the code-stack.
public func fetchMostRecentSamples(ofTypes types: [HKSampleType], completion: #escaping HMTypedSampleBlock)
{
let group = DispatchGroup()
var samples = [HKSampleType: [MCSample]]()
let updateSamples : ((MCSampleArray, CacheExpiry) -> Void, (MCError) -> Void, HKSampleType, [MCSample], MCError) -> Void = {
(success, failure, type, statistics, error) in
guard error == nil else {
failure(error)
return
}
guard statistics.isEmpty == false else {
failure(error)
return
}
samples[type] = statistics
success(MCSampleArray(samples: statistics), .never)
}
let onStatistic : ((MCSampleArray, CacheExpiry) -> Void, (MCError) -> Void, HKSampleType) -> Void = { (success, failure, type) in
self.fetchMostRecentSample(type) { (samples, error) in
guard error == nil else {
failure(error)
return
}
Then fetchMostRecentSample has this header:
public func fetchMostRecentSample(_ sampleType: HKSampleType, completion: #escaping HMSampleBlock)
and the error message on the failure is "Closure use of non-escaping parameter 'failure' may allow it to escape" : "Parameter 'failure' is implicitly non-escaping"
Note that the let updateSamples is fine (not calling another function), but that the onStatistic with the failure (having error codes) is where the problem with escaping/non-escaping is coming from. MCError is an enum with the error codes (refactored from NSError? in Swift 2.3 version).
Major credits to Hamish for helping me to see into this. In case it helps others coming across this on their Swift 3 conversions, the other place that I stumbled was in assuming that all of our third-party pods were complete if they stated ready for Swift 3. In this case I had to update the AwesomeCache code to handle the errors correctly:
open func setObject(forKey key: String, cacheBlock: #escaping (#escaping(CacheBlockClosure), #escaping(ErrorClosure)) -> Void, completion: #escaping (T?, Bool, NSError?) -> Void) {
if let object = object(forKey: key) {
completion(object, true, nil)
} else {
let successBlock: CacheBlockClosure = { (obj, expires) in
self.setObject(obj, forKey: key, expires: expires)
completion(obj, false, nil)
}
let failureBlock: ErrorClosure = { (error) in
completion(nil, false, error)
}
cacheBlock(successBlock, failureBlock)
}
}
This is simply adding two more #escaping beyond what was in the github master. Otherwise, as Hamish pointed out, it comes down to paying attention to where #escaping needs to be added on the function calls. The update for AwesomeCache came from code like this:
aggregateCache.setObjectForKey(key, cacheBlock: { success, failure in
let doCache : ([MCAggregateSample], NSError?) -> Void = { (aggregates, error) in
guard error == nil else {
failure(error)
return
}
success(MCAggregateArray(aggregates: aggregates), .Date(self.getCacheExpiry(period)))
}
where the success, failure will be flagged as potentially escaping without the code changes on the AwesomeCache.

Swift 3 Cannot convert value of type '(String?,Error?) -> Void!' to expected argument type '((String?,Error?) -> Void)!

I am working with the LayerKit API and once I converted the project to Swift 3, I got this weird error.
The code written is.
func authenticateLayerWithUserID(userID: String, completion: #escaping ((_ success: Bool, _ error: NSError?) -> Void)) {
if let layerClient = layerClient {
if layerClient.authenticatedUserID != nil {
print("Layer Authenticated as User \(layerClient.authenticatedUserID)")
completion(true, nil)
return
}
// Authenticate with Layer
// See "Quick Start - Authenticate" for more details
// https://developer.layer.com/docs/quick-start/ios#authenticate
/*
* 1. Request an authentication Nonce from Layer
*/
layerClient.requestAuthenticationNonceWithCompletion() { (nonce: String?, error: Error?) -> Void! in
if nonce.isEmpty {
completion(success: false, error: error)
return
}
the "layerClient.requestAuthenticationNonceWithCompletion" line is
throwing the error: "Cannot convert value of type '(String?,Error?) ->
Void!' to expected argument type '((String?,Error?) -> Void)!'"
If I add parenthesis to match the syntax of the error, that throws more errors. How can I fix this?

Swift 3 suggested correction for "cannot convert value for '(CKAccountStatus, NSError?) -> ()' to '... -> Void'

I am getting the error cannot convert value for '(CKAccountStatus, NSError?) -> ()' to '(CKAccountStatus, NSError?) -> Void' on the third line of the function.
Here is the code:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
container.accountStatusWithCompletionHandler{
(status: CKAccountStatus, error: NSError?) in
dispatch_async(dispatch_get_main_queue(), {
var title: String!
var message: String!
if error != nil{
title = "Error"
message = "An error occurred = \(error)"
} else {
//title = "No errors occurred getting info"
switch status{
case .Available:
message = "The user is logged in to iCloud"
title = "GOOD"
print("determined status was available")
self.shouldPullFromICloud()
//self.displayAlertWithTitle(title, message: message)
case .CouldNotDetermine:
message = "Could not determine if the user is logged" +
" into iCloud or not"
title = "BAD"
self.noUserIsSignedIn()
case .NoAccount:
message = "User is not logged into iCloud"
title = "BAD"
self.noUserIsSignedIn()
case .Restricted:
message = "Could not access user's iCloud account information"
title = "BAD"
self.noUserIsSignedIn()
}
print(title, message)
}
})
}
}
Now Xcode is offering a 'fix it' by inserting "as! (CKAccountStatus, Error?) -> Void" on the second to last line. This doesn't help and it just keeps asking me if I want to 'fix it' and if I click it it will just keep tacking on more and more "as! (CKAccountStatus, Error?) -> Void"s. Here is what it looks like after a single click of the 'fix it' button:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
container.accountStatusWithCompletionHandler{
(status: CKAccountStatus, error: NSError?) in
dispatch_async(dispatch_get_main_queue(), {
var title: String!
var message: String!
if error != nil{
title = "Error"
message = "An error occurred = \(error)"
} else {
//title = "No errors occurred getting info"
switch status{
case .Available:
message = "The user is logged in to iCloud"
title = "GOOD"
print("determined status was available")
self.shouldPullFromICloud()
//self.displayAlertWithTitle(title, message: message)
case .CouldNotDetermine:
message = "Could not determine if the user is logged" +
" into iCloud or not"
title = "BAD"
self.noUserIsSignedIn()
case .NoAccount:
message = "User is not logged into iCloud"
title = "BAD"
self.noUserIsSignedIn()
case .Restricted:
message = "Could not access user's iCloud account information"
title = "BAD"
self.noUserIsSignedIn()
}
print(title, message)
}
})
} as! (CKAccountStatus, Error?) -> Void
}
First two things:
accountStatusWithCompletionHandler(_:) has renamed to accountStatus(completionHandler:) in Swift 3.
func accountStatus(completionHandler: (CKAccountStatus, Error?) -> Void)
And as already commented, the type of the completeHandler has changed:
completionHandler: #escaping (CKAccountStatus, Error?) -> Void
When supplying a closure argument with closure expression, you have no need to worry about #escaping.
So, you need to replace these two lines:
container.accountStatusWithCompletionHandler{
(status: CKAccountStatus, error: NSError?) in
With these:
container.accountStatus {
(status: CKAccountStatus, error: Error?) in
You may find many other parts you need to fix in Swift 3, but with the two above fixed, Xcode will give you a better suggestion.
(Do not forget to remove as! (CKAccountStatus, Error?) -> Void, before testing the code above.)
Reason for the error.
This happened to me when I updated my project to swift 3.0 or a cocoapod. To fix this copy the method code and then redeclare it using autocomplete and then place the code back inside. The error is a bit vague but this is because the declaration has changed.
Quick Fix
Specifically the declaration now uses Error not NSError. So change the line:
(status: CKAccountStatus, error: NSError?) in
To:
(status: CKAccountStatus, error: Error?) in
Full Fix (Requested by OP)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
container.accountStatus{
(status: CKAccountStatus, error: Error?) in
dispatch_async(dispatch_get_main_queue(), {
var title: String!
var message: String!
if error != nil{
title = "Error"
message = "An error occurred = \(error)"
} else {
//title = "No errors occurred getting info"
switch status{
case .Available:
message = "The user is logged in to iCloud"
title = "GOOD"
print("determined status was available")
self.shouldPullFromICloud()
//self.displayAlertWithTitle(title, message: message)
case .CouldNotDetermine:
message = "Could not determine if the user is logged" +
" into iCloud or not"
title = "BAD"
self.noUserIsSignedIn()
case .NoAccount:
message = "User is not logged into iCloud"
title = "BAD"
self.noUserIsSignedIn()
case .Restricted:
message = "Could not access user's iCloud account information"
title = "BAD"
self.noUserIsSignedIn()
}
print(title, message)
}
})
}
}

func writeImageToFile(path: String, completeBlock: (success: Bool) -> Void){ }

i am trying to upload images using DKimagepickercontroller and this is a way to upload it to a url but i am confuse what i am suppose to put into the completeBlock: (success: Bool) - Void
func writeImageToFile(path: String, completeBlock: (success: Bool) -> Void){
}
this is the code that i wrote in xcode
let apath = "http://localhost/swift/upload.php"
writeImageToFile(apath,completeBlock: (success: false) -> Void)
but i got this error
expected expression in the list of expressions and this error expexted ',' separator
You have to call it like that writeImageToFile(path) { success in print(success) }
This block is supposed to be used as a trailing closure, don't leave it in the signature.
It looks like this in Xcode: stackoverflow.com/a/33020097/2227743
The closure argument is used once the task is done, in your case it will indicate if the file has been written or not.
You can for example test success in the closure:
writeImageToFile(somePath) { (success) in
if success {
// the file has been written
} else {
// the file hasn't been written
}
}

Stripe error "create token with card(_:completion:) is unavailable"

In this line, the error was Display. Can someone tell me what mistake was made?
Stripe.createTokenWithCard(card, completion: { (token: STPToken!, error: NSError!) -> Void in
self.handleToken(token)
I had the same problem after updating Stripe in pods recently. That method is deprecated. Instead, you can use the following code:
STPAPIClient.sharedClient().createTokenWithCard(card, completion: { (token: STPToken!, error: NSError!) -> Void in
})
It takes the same parameters.
Update
Thanks to #Christine and #Keyhole150
This function in Stripe API has now be changed to
STPAPIClient.sharedClient().createTokenWithCard(card, completion: { (token: STPToken?, error: NSError?) -> Void in
})
Thanks, #Shali! Your tip is helpful.
For those who are beginners like me, you might still get an error. In case you experience an error indicating either an extra argument in call before adding sharedClient() or how createTokenWithCard cannot be invoked after sharedClient() is added, it helps to make the completion arguments optional (as in STPToken? and NSError?).
As mentioned by Christine, the method now uses Optionals so it looks like the following:
STPAPIClient.sharedClient().createTokenWithCard(card, completion: { (token: STPToken?, error: NSError?) -> Void in
})
For objective-c using latest stripe pod
#import "Stripe.h"
STPCardParams *cardParams = [[STPCardParams alloc] init];
cardParams.number = #"4242424242424242";
cardParams.expMonth = 10;
cardParams.expYear = 2018;
cardParams.cvc = #"123";
[[STPAPIClient sharedClient] createTokenWithCard:cardParams completion:^(STPToken *token, NSError *error) {
if (token == nil || error != nil) {
// Present error to user...
NSLog(#"%#",error.description);
return;
}
NSLog(#"token.tokenId :: %#",token.tokenId);
}];