Return value from swift function with background code - xcode

The function below is only returning false, probably because the user.singUpInBackgroundWithBlock is happening in background.
Is there anyway that I can get the intended return value from the function?
var returnFlag:Bool = false
func signUpAction(email:String, password:String) -> Bool
{
let user = PFUser()
user.username = email
user.password = password
user.signUpInBackgroundWithBlock {
(succeeded: Bool, error: NSError?) -> Void in
if let error = error {
let errorString = error.userInfo["error"] as? NSString
print(errorString!)
// INTENDED RETURN
self.returnFlag = false
} else {
// INTENDED RETURN
self.returnFlag = true
}
}
return self.returnFlag
}

You are right, the function will return false because the block is probably not called on the main thread.
This is an ideal scenario to use NSNotification. Do not pass a return value, instead post a notification once the login action is complete.
Sample Code :
Somewhere early in your view controller lifecycle, preferably viewdidload. Register your class to observer for login success and login failed notifications.
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("LoginFailedFunc:"), name: "loginFailed", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("loginSuccessFunc:"), name: "loginSuccess", object: nil)
In your login function, post a notification based on valid or invalid login.
func signUpAction(email:String, password:String){
let user = PFUser()
user.username = email
user.password = password
user.signUpInBackgroundWithBlock {
(succeeded: Bool, error: NSError?) -> Void in
if let error = error {
let errorString = error.userInfo["error"] as? NSString
print(errorString!)
// INTENDED RETURN
NSNotificationCenter.defaultCenter().postNotificationName("loginFailed", object: self)
self.returnFlag = false
} else {
NSNotificationCenter.defaultCenter().postNotificationName("loginSuccess", object: self)
self.returnFlag = true
}
}
}
Finally remember to stop observing notificaitons, when no longer required.

Related

Square Connect SDK opens a blank page

I am attempting to implement Square Connect iOS SDK, and after implementing and clicking the pay button it opens up the Square Payment app and redirects to a blank page .. have you guys had the same issues ?
My App delegate has the proper section:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
guard let sourceApplication = options[.sourceApplication] as? String,
sourceApplication.hasPrefix("com.squareup.square") else {
return false
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}
I have created a proper URL scheme inside the Square portal. Also my view controller has the correct code:
func charge()
{
// connect v1
if let callbackURL = URL(string: "myscheme://")
{
do
{
SCCAPIRequest.setClientID("xxxxxxxxxxx")
let amount = try SCCMoney(amountCents: 100, currencyCode: "USD")
let request = try SCCAPIRequest(
callbackURL: callbackURL,
amount: amount,
userInfoString: nil,
locationID: nil,
notes: "Purchase for cleaning",
customerID: nil, supportedTenderTypes: .all,
clearsDefaultFees: true,
returnAutomaticallyAfterPayment: true)
try SCCAPIConnection.perform(request)
}
catch let error as NSError {
print(error.localizedDescription)
}
}
}
https://snag.gy/R4A30L.jpg
Andrei
You need to make sure you delete all apps from the phone with having duplicate bundle ids. This way you will make sure you go back to the original app which triggered the payment.

Receive promised e-mail in macOS 10.12+

Previously, I was using the following to discover e-mail meta-data from a drag & dropped e-mail(/-thread) from Mail.app.
if let filenames = draggingInfo.namesOfPromisedFilesDropped(atDestination: URL(fileURLWithPath: destinationDir!)) {
/// TODO: in future implementation Mail might return multiple filenames here.
/// So we will keep this structure to iterate the filenames
//var aPaths: [String] = []
//for _ in filenames {
if let aPath = pb.string(forType: "com.apple.pasteboard.promised-file-url") {
return aPath
}
//}
//return aPaths
}
Kind of janky, but it worked, since "com.apple.pasteboard.promised-file-url" was only supplied in those situations.
Since 10.12 however, the API seems to have changed, and looking at the WWDC2016 talk it appears that Apple wants us to use NSFilePromiseReceiver now.
I've tried a couple of approaches but I can't get a promised file URL to pop out.
Setup:
class DropzoneView: NSView {
var supportedDragTypes = [
kUTTypeURL as String, // For any URL'able types
"public.url-name", // E-mail title
"public.utf8-plain-text", // Plaintext item / E-mail thread title / calendar event date placeholder
"com.apple.pasteboard.promised-file-content-type", // Calendar event / Web URL / E-mail thread type detection
"com.apple.mail.PasteboardTypeMessageTransfer", // E-mail thread detection
"NSPromiseContentsPboardType", // E-mail thread meta-data
"com.apple.pasteboard.promised-file-url", // E-mail thread meta-data
"com.apple.NSFilePromiseItemMetaData" // E-mail thread meta-data
]
override func viewDidMoveToSuperview() {
var dragTypes = self.supportedDragTypes.map { (type) -> NSPasteboard.PasteboardType in
return NSPasteboard.PasteboardType(type)
} // Experiment:
dragTypes.append(NSPasteboard.PasteboardType.fileContentsType(forPathExtension: "eml"))
dragTypes.append(NSPasteboard.PasteboardType.fileContentsType(forPathExtension: "emlx"))
self.registerForDraggedTypes(dragTypes)
}
}
Handling:
extension DropzoneView {
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
return .copy
}
override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation {
return .copy
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
let pasteboard: NSPasteboard = sender.draggingPasteboard()
guard let filePromises = pasteboard.readObjects(forClasses: [NSFilePromiseReceiver.self], options: nil) as? [NSFilePromiseReceiver] else {
return false
}
var files = [Any]()
var errors = [Error]()
let filePromiseGroup = DispatchGroup()
let operationQueue = OperationQueue()
let newTempDirectoryURL = URL(fileURLWithPath: (NSTemporaryDirectory() + (UUID().uuidString) + "/"), isDirectory: true)
do {
try FileManager.default.createDirectory(at: newTempDirectoryURL, withIntermediateDirectories: true, attributes: nil)
}
catch {
return false
}
// Async attempt, either times out after a minute or so (Error Domain=NSURLErrorDomain Code=-1001 "(null)") or gives 'operation cancelled' error
filePromises.forEach({ filePromiseReceiver in
filePromiseGroup.enter()
filePromiseReceiver.receivePromisedFiles(atDestination: newTempDirectoryURL,
options: [:],
operationQueue: operationQueue,
reader: { (url, error) in
Swift.print(url)
if let error = error {
errors.append(error)
}
else if url.isFileURL {
files.append(url)
}
else {
Swift.print("No loadable URLs found")
}
filePromiseGroup.leave()
})
})
filePromiseGroup.notify(queue: DispatchQueue.main,
execute: {
// All done, check your files and errors array
Swift.print("URLs: \(files)")
Swift.print("errors: \(errors)")
})
Swift.print("URLs: \(files)")
return true
}
Other attempts:
// returns nothing
if let filenames = pasteboard.propertyList(forType: NSPasteboard.PasteboardType(rawValue: "com.apple.pasteboard.promised-file-url")) as? NSArray {
Swift.print(filenames)
}
// doesn't result in usable URLs either
if let urls = pasteboard.readObjects(forClasses: [NSPasteboardItem.self /*NSURL.self, ???*/], options: [:]) as? [...
Any pointers would be greatly appreciated.
I have managed to get the file to "pop out" but I cannot get the details for them. It transfers immediately and then hangs for 60 seconds before returning an error message.
Maybe it's a clue but the checkExtension method never returns unless commented out and set to true.
Hopefully this helps kick the can down the road a bit:
class DropView: NSView
{
var filePath: String?
required init?(coder: NSCoder) {
super.init(coder: coder)
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.red.cgColor
registerForDraggedTypes([NSPasteboard.PasteboardType
.fileNameType(forPathExtension: ".eml"), NSPasteboard.PasteboardType.filePromise])
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
if checkExtension(sender) == true
{
self.layer?.backgroundColor = NSColor.blue.cgColor
return .copy
}
else
{
return NSDragOperation()
}
}
fileprivate func checkExtension(_ drag: NSDraggingInfo) -> Bool
{
return true
// guard let board = drag.draggingPasteboard().propertyList(forType: NSPasteboard.PasteboardType(rawValue: "com.apple.mail.PasteboardTypeMessageTransfer")) as? NSArray,
// let path = board[0] as? String
// else
// {
// return false
// }
//
// let suffix = URL(fileURLWithPath: path).pathExtension
// for ext in self.expectedExt
// {
// if ext.lowercased() == suffix
// {
// return true
// }
// }
// return false
}
override func draggingExited(_ sender: NSDraggingInfo?)
{
self.layer?.backgroundColor = NSColor.gray.cgColor
}
override func draggingEnded(_ sender: NSDraggingInfo)
{
self.layer?.backgroundColor = NSColor.gray.cgColor
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool
{
let pasteboard: NSPasteboard = sender.draggingPasteboard()
guard let filePromises = pasteboard.readObjects(forClasses: [NSFilePromiseReceiver.self], options: nil) as? [NSFilePromiseReceiver] else {
return false
}
print ("Files dropped")
var files = [URL]()
let filePromiseGroup = DispatchGroup()
let operationQueue = OperationQueue()
let destURL = URL(fileURLWithPath: "/Users/andrew/Temporary", isDirectory: true)
print ("Destination URL: \(destURL)")
filePromises.forEach ({ filePromiseReceiver in
print (filePromiseReceiver)
filePromiseGroup.enter()
filePromiseReceiver.receivePromisedFiles(atDestination: destURL,
options: [:],
operationQueue: operationQueue,
reader:
{ (url, error) in
print ("Received URL: \(url)")
if let error = error
{
print ("Error: \(error)")
}
else
{
files.append(url)
}
print (filePromiseReceiver.fileNames, filePromiseReceiver.fileTypes)
filePromiseGroup.leave()
})
})
filePromiseGroup.notify(queue: DispatchQueue.main,
execute:
{
print ("Files: \(files)")
print ("Done")
})
return true
}
}
The output of this is a bit weird. The url variable aways repeats the name of the directory that I passed in eg
Files dropped
Destination URL: file:///Users/andrew/Temporary/
<NSFilePromiseReceiver: 0x6000000a1aa0>
** one minute gap **
Received URL: file:///Users/andrew/Temporary/Temporary/
Error: Error Domain=NSURLErrorDomain Code=-1001 "(null)"
["Temporary"] ["com.apple.mail.email"]
Files: []
Done
I saw this error when trying to receive promised files to an invalid destination url.
In my case I was using Ole Begemann's Temporary File Helper and accidentally letting it go out of scope, which deleted the directory before anything could be copied.
receivePromisedFiles gave me the -1001 timeout error after a long wait, but it did still pass a URL that would have been correct given my inputs. Obviously no file was at that location.
When I changed to a valid url all worked as expected. It might be worth checking Sandbox issues etc.
Apple now have some useful example projects in the File Promises section here:
https://developer.apple.com/documentation/appkit/documents_data_and_pasteboard

OSStatus Code -1009, com.apple.LocalAuthentication

I'm trying to test encryption using the iOS keychain.
Domain=com.apple.LocalAuthentication Code=-1009 "ACL operation is not allowed: 'od'" UserInfo={NSLocalizedDescription=ACL operation is not allowed: 'od'}
This is my test code:
func testEncrpytKeychain() {
let promise = expectation(description: "Unlock")
let data: Data! = self.sampleData
let text: String! = self.sampleText
wait(for: [promise], timeout: 30)
let chain = Keychain(account: "tester", serviceName: "testing2", access: .whenPasscodeSetThisDeviceOnly, accessGroup: nil)
chain.unlockChain { reply, error in
defer {
promise.fulfill()
}
guard error == nil else {
// ** FAILS ON THIS LINE WITH OSSTATUS ERROR **
XCTAssert(false, "Error: \(String(describing: error))")
return
}
guard let cipherData = try? chain.encrypt(data) else {
XCTAssert(false, "Cipher Data not created")
return
}
XCTAssertNotEqual(cipherData, data)
guard let clearData = try? chain.decrypt(cipherData) else {
XCTAssert(false, "Clear Data not decrypted")
return
}
XCTAssertEqual(clearData, data)
let clearText = String(data: clearData, encoding: .utf8)
XCTAssertEqual(clearText, text)
}
}
And this is the underlying async unlockChain code:
// context is a LAContext
func unlockChain(_ callback: #escaping (Bool, Error?) -> Void) {
var error: NSError? = nil
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else {
callback(false, error)
return
}
context.evaluateAccessControl(control, operation: .createItem, localizedReason: "Access your Account") { (reply, error) in
self.context.evaluateAccessControl(self.control, operation: .useItem, localizedReason: "Access your Account") { (reply, error) in
self.unlocked = reply
callback(reply, error)
}
}
}
Here is how the context and control objects are made
init(account: String, serviceName: String = (Bundle.main.bundleIdentifier ?? ""), access: Accessibility = .whenUnlocked, accessGroup: String? = nil) {
self.account = account
self.serviceName = serviceName
self.accessGroup = accessGroup
self.access = access
var error: Unmanaged<CFError>? = nil
self.control = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
access.attrValue,
[.privateKeyUsage],
&error)
if let e: Error = error?.takeRetainedValue() {
Log.error(e)
}
self.context = LAContext()
}
I can't find a single bit of information about this error:
Domain=com.apple.LocalAuthentication Code=-1009
the OSStatus Code site doesn't contain anything for it either
any help is appreciated, thanks.
I solved the same issue by removing the previous private key before creating a new one.
I would guess that on iOS10 (11 was not showing up the error), when you SecKeyCreateRandomKey(...) with the same tag/size but not the same access settings, it would just return true but use the old one (feels odd but who knows)?
Here is a lazy C function I just made to remove it (just remember to set your ApplicationPrivateKeyTag:
void deletePrivateKey()
{
CFStringRef ApplicationPrivateKeyTag = CFSTR("your tag here");
const void* keys[] = {
kSecAttrApplicationTag,
kSecClass,
kSecAttrKeyClass,
kSecReturnRef,
};
const void* values[] = {
ApplicationPrivateKeyTag,
kSecClassKey,
kSecAttrKeyClassPrivate,
kCFBooleanTrue,
};
CFDictionaryRef params = CFDictionaryCreate(kCFAllocatorDefault, keys, values, (sizeof(keys)/sizeof(void*)), NULL, NULL);
OSStatus status = SecItemDelete(params);
if (params) CFRelease(params);
if (ApplicationPrivateKeyTag) CFRelease(ApplicationPrivateKeyTag);
if (status == errSecSuccess)
return true;
return false;
}
FWIW: it looks like apple updated their doc about the Security Framework and the SecureEnclave, it's a bit easier to understand now.

What does [weak self] do and what is this code structure means?

I have couple of questions about the structure of the following code.I assume progressBlock and completionhandlers are callback functions passed to downloadWithDownloadType function. Is my assumption correct? And What does [weak Self] before function parameters do? In what situation do you need that?
func downloadContent(key: String, pinOnCompletion: Bool) {
let manager = AWSUserFileManager.defaultUserFileManager()
let content = manager.contentWithKey(self.prefix + key)
content.downloadWithDownloadType(
.IfNewerExists,
pinOnCompletion: pinOnCompletion,
progressBlock: {[weak self](content: AWSContent?, progress: NSProgress?) -> Void in
guard self != nil else { return }
/* Show progress in UI. */
},
completionHandler: {[weak self](content: AWSContent?, data: NSData?, error: NSError?) -> Void in
guard self != nil else { return }
if let error = error {
// Handle Error
return
}
if let fileData = data {
let rawData = NSString(data: fileData, encoding:NSUTF8StringEncoding) as! String
// Do something
}
//Download Complete
})
}

This application is modifying the autolayout engine from a background thread, which can lead to engine corruption

I am getting this error This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes.This will cause an exception in a future release. I don't know what is causing this error. Can anybody help me.
func getUserDataFromTwitterWithUser(user : PFUser)
{
//NRLoader.showLoader()
let strTwURL = "https://api.twitter.com/1.1/users/show.json? screen_name="+PFTwitterUtils.twitter()!.screenName! + "&access_token="+PFTwitterUtils.twitter()!.authToken!
let twURL = NSURL (string: strTwURL)
let request = NSMutableURLRequest(URL: twURL!, cachePolicy: NSURLRequestCachePolicy.UseProtocolCachePolicy, timeoutInterval: 2.0) as NSMutableURLRequest
PFTwitterUtils.twitter()?.signRequest(request)
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if error == nil {
var jsonOptional = Dictionary<String, AnyObject>()
do {
jsonOptional = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers ) as! Dictionary<String, AnyObject>
// use jsonData
} catch {
// report error
}
var userName = ""
if let screenName = jsonOptional["screen_name"] as? String{
userName = screenName
}
else if let name = jsonOptional["name"] as? String{
userName = name
}
var profilePicUrl = ""
if let picUrl = jsonOptional["profile_image_url"] as? String{
profilePicUrl = picUrl
}
AppUser.currentUser()?.username = userName
AppUser.currentUser()?.profileAwsURL = profilePicUrl
//NRLoader.hideLoader()
//if ParseUtils.isLoggedInUserIsAnonymous() {
let signUpVC:SignMeUpViewController = self.storyboard!.instantiateViewControllerWithIdentifier("SignMeUpViewController") as! SignMeUpViewController
signUpVC.isFromLogin = true
self.navigationController!.pushViewController(signUpVC, animated: true)
//} else {
// self.pushToSubmitDreamViewController()
//}
}
else {
//NRLoader.hideLoader()
NRToast.showToastWithMessage(error!.description)
}
}).resume()
}
The dataTaskWithRequest call runs in the background and then calls your completion handler from the same thread. Anything that updates the UI should run on the main thread, so all of your current handler code should be within a dispatch_async back onto the main queue:
dispatch_async(dispatch_get_main_queue()) {
// Do stuff to UI
}
Swift 3:
DispatchQueue.main.async() {
// Do stuff to UI
}
Therefore, ideally all the code you currently have within if error == nil should be off in another function, say called handleRequest, so your current code becomes:
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if error == nil {
dispatch_async(dispatch_get_main_queue(), {
self.handleRequest(...)I
})
}
Swift 3
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if error == nil {
DispatchQueue.main.async {
self.handleRequest(...)I
}
}
Should try Symbolic Breakpoint to detect the issue:-
Then put your UI Update code in main thread
DispatchQueue.main.async {}
You'd better change UI only in the main thread
swift3,
let liveInfoUrl = URL(string: "http://192.168.1.66/api/cloud/app/liveInfo/7777")
let task = URLSession.shared.dataTask(with: liveInfoUrl! as URL) {data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async {
print(String(data: data, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) ?? "aaaa")
//do some ui work
}
}
if the above suggestions still give you no joy then the sure-est way is to redesign your functions so that getting what you need with
URLSession.shared.dataTask
then hands over so a variable declared outside that function, then a separate UIControl ( button, swipe etc ) displays it to a label or textview or whatever.
After all that is what the error message is telling you. they're separate concerns

Resources