I am having trouble analyzing the results given by the callback of GMSPlacesClient findAutocompletePredictionsFromQuery:, which is a GMSAutocompletePrediction array.
The app will crash on let searchResultsWayPoints = finalResults.map ,
stating
Precondition failed: NSArray element failed to match the Swift Array Element type
Expected GMSAutocompletePrediction but found GMSAutocompletePrediction
func getGoogleWayPoint(text: String) -> Promise<[GoogleWayPoint]> {
return Promise<[GoogleWayPoint]> { resolver in
if text.isEmpty {
resolver.fulfill([])
return
}
placesClient.findAutocompletePredictions(fromQuery: text, filter: filter, sessionToken: sessionToken) { (results, error) in
if let error = error {
dprint(error.localizedDescription)
resolver.fulfill([])
return
}
guard let finalResults = results else {
dprint("can't found results")
resolver.fulfill([])
return
}
let searchResultsWayPoints = finalResults.map {
GoogleWayPoint(id: $0.placeID, address: $0.attributedFullText.string)
}
resolver.fulfill(searchResultsWayPoints)
}
}
}
Any help would be appreciated. Thank you.
So I have solved this problem, but I did not fix anything, since it is from the GooglePlaces framework.
In order to solve this, simply make results as results:[Any]? at the callback.
Then, at guard let, safely convert it to [GMSAutocompletePrediction].
Here is the complete code
func getGoogleWayPoint(text: String) -> Promise<[GoogleWayPoint]> {
return Promise<[GoogleWayPoint]> { resolver in
if text.isEmpty {
resolver.fulfill([])
return
}
placesClient.findAutocompletePredictions(fromQuery: text, filter: filter, sessionToken: sessionToken) { (results: [Any]?, error) in
if let error = error {
dprint(error.localizedDescription)
resolver.fulfill([])
return
}
guard let finalResults = results as? [GMSAutocompletePrediction] else {
dprint("can't found results")
resolver.fulfill([])
return
}
let searchResultsWayPoints = finalResults.map {
GoogleWayPoint(id: $0.placeID, address: $0.attributedFullText.string)
}
resolver.fulfill(searchResultsWayPoints)
}
}
}
Related
Not sure why i'm getting this.. any suggestions would be grateful!
I ran into issues with my original coding where I had Firebase pod and Firebase Package.. so I started from scratch since that wasnt fixing itself.. now I get this.. and I am at a loss for how to resolve it.
static func fetchUsers() -> AnyPublisher<[UserProfile], Error> {
Future< [UserProfile], Error > { promise in
self.db.collection("Users")
.getDocuments { (snapshot, error) in
if let error = error {
promise(.failure(error))
return
}
guard let snapshot = snapshot else {
promise(.failure(FirebaseError.badSnapshot))
return
}
var users = [UserProfile]()
snapshot.documents.forEach { document in
print(users.count)
if let user = try? document.data(as: UserProfile.self){
if users.contains(where: { $0.id == user.id}) {return}
users.append(user)
} else {
print("Not working")
}
}
promise(.success(users))
}
}
.eraseToAnyPublisher()
}
I believe this is the syntax you're after:
var users = [UserProfile]()
users = snapshot.documents.compactMap { (document) -> UserProfile? in
if users.contains(where: { $0.id == user.id}) {
return nil
} else {
return try? document.data(as: UserProfile.self)
}
}
Also be aware that when you iterate something in Swift and encounter a false condition on an iteration, return will return out of the greater scope, not just that iteration. Therefore, use continue.
for x in y {
guard x > 0 else {
continue // continues loop
}
// ...
}
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
after upgrading to new version of Xcode/swift i am getting error "Ambiguous use of subscript before was work well but now i am getting this error
my code for search in my data
#IBAction func searchB(sender: UITextField) {
dataSecond = []
if sender.text == "" {
search = false
self.reload()
} else {
for i in data {
if (i["name"] as! String!).lowercaseString.findInString(sender.text!) { //Here is the error in this if condition
dataSecond.addObject(i)
}
}
search = true
self.reload()
}
Swift 2.2 is rather strict about types, much more strict than the previous versions. What was implicit before has now to be explicit.
Use safe unwrapping and downcasting and it should work:
if let senderText = sender.text {
for i in data {
if let name = i["name"] as? String {
if name.lowercaseString.findInString(senderText) {
dataSecond.addObject(i)
}
}
}
}
I'm using the SODA Client for swift (Created by Socrata), I just updated to XCode 7 and swift 2 and found some troubles. The one I haven't been able to solve is the completion handler case when it finds an error, it's not accepting the line "syncCompletion(.Error (reqError))" that supposedly should get the error and return to main thread.
I've seen many errors with the same description here "Type of expression is ambiguous without more context", but not in completion handlers, I saw one using do - catch that is different. I'm don't know enough of swift to find out the way to change this.
Some answers suppose you should rewrite the code because some types could have change in swift 2, but I wouldn't know where to start rewriting.
Thanks in advance for your help.
var task = session.dataTaskWithRequest(request, completionHandler: { data, response, reqError in
// We sync the callback with the main thread to make UI programming easier
let syncCompletion = { res in NSOperationQueue.mainQueue().addOperationWithBlock { completionHandler (res) } }
// Give up if there was a net error
if reqError != nil {
syncCompletion(.Error (reqError))
return
}
// Try to parse the JSON
// println(NSString (data: data, encoding: NSUTF8StringEncoding))
var jsonError: NSError?
var jsonResult: AnyObject!
do {
jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
} catch var error as NSError {
jsonError = error
jsonResult = nil
} catch {
fatalError()
}
if let error = jsonError {
syncCompletion(.Error (error))
return
}
// Interpret the JSON
if let a = jsonResult as? [[String: AnyObject]] {
syncCompletion(.Dataset (a))
}
else if let d = jsonResult as? [String: AnyObject] {
if let e : AnyObject = d["error"] {
if let m : AnyObject = d["message"] {
syncCompletion(.Error (NSError(domain: "SODA", code: 0, userInfo: ["Error": m])))
return
}
}
syncCompletion(.Dataset ([d]))
}
else {
syncCompletion(.Error (NSError(domain: "SODA", code: 0, userInfo: nil)))
}
})
Solved, I replaced:
if reqError != nil
With
if let error = reqError
and:
syncCompletion(.Error (reqError))
with
syncCompletion(.Error (error))
I am trying to print out the value "username" from my coredata entity.
var request = NSFetchRequest(entityName: "Users")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
if (results?.count > 0) {
for result: AnyObject in results! {
println(result.username)
}
}
The line println(result.username) is giving me a compile error of 'AnyObject' does not have a member named 'username'.
You have to cast the array of managed object to the correct type:
for result in results! as [Users] {
println(result.username)
}
This assumes that you have created a managed object subclass for the "Users" entity.
You should also distinguish whether executeFetchRequest() returned nil
(i.e. the fetch request failed), or 0 (i.e. no objects found),
and use the error parameter:
var error : NSError?
if let results = context.executeFetchRequest(request, error: &error) {
if (results.count > 0) {
for result in results as [Users] {
println(result.username)
}
} else {
println("No Users")
}
} else {
println("Fetch failed: \(error)")
// Handle error ...
}
Update for Swift 2/Xcode 7 with try/catch error handling:
do {
let results = try context.executeFetchRequest(request) as! [Users]
if (results.count > 0) {
for result in results {
print(result.username)
}
} else {
print("No Users")
}
} catch let error as NSError {
// failure
print("Fetch failed: \(error.localizedDescription)")
}
Note that the forced cast as! [Users] is acceptable here.
The returned objects are always instances of the corresponding class as configured in the Core Data model inspector, otherwise you have
a programming error which should be detected early.
Martin's answer definitely lets you access the properties of your object, but the cast is forced. Like it or not, Swift's strong type system is the future. When returning results from a fetch request, you might consider testing for the type.
func executeFetchRequestT<T:AnyObject>(request:NSFetchRequest, managedObjectContext:NSManagedObjectContext, error: NSErrorPointer = nil) -> [T]? {
var localError: NSError? = nil
if let results:[AnyObject] = managedObjectContext.executeFetchRequest(request, error: &localError) {
if results.count > 0 {
if results[0] is T {
let casted:[T] = results as [T]
return .Some(casted)
}
if error != nil {
error.memory = NSError(domain: "error_domain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Object in fetched results is not the expected type."])
}
} else if 0 == results.count {
return [T]() // just return an empty array
}
}
if error != nil && localError != nil {
error.memory = localError!
}
return .None
}
Using this approach you can type your results and get an error if the type is incorrect.
var fetchError:NSError? = nil
if let results:[Users] = executeFetchRequestT(fetchRequest, managedObjectContext: managedObjectContext, error: &fetchError) {
for user in results {
// access the results with confidence of the correct type
}
} else {
// should have an error condition, handle it appropriately
assertFailure("something bad happened")
}
Change your for loop to this
for result: AnyObject in results! {
if let user: AnyObject = result.valueForKey("username") {
println(user)
}
}
The fix is using valueForKey("String")