getting information outside of .observe method in swift Firebase - xcode

So i was able to retrieve all data from my firebase database using the following function. I was wondering if there is a way i can save the output which is "print(name)" in the self.GetUsername into a global variable
func GetUsername(uid:String , completion: #escaping ([Int]) -> ()) {
Database.database().reference().child("Songs").observeSingleEvent(of: .value) { (snapshot:DataSnapshot) in
var array: [Int] = []
for item in snapshot.children.allObjects as! [DataSnapshot]
{
array.append(item.value as! Int)
}
completion(array)
}
}
self.GetUsername(uid: (Auth.auth().currentUser?.uid)!) { (name) in
print(name)
}

Related

SwiftUI: How to drag and drop an email from Mail on macOS

As a follow up on #Asperi's answer of my question on how to drag and drop contacts and, I'd also like to be able to drag and drop email in the same way. Here is my code:
import SwiftUI
import UniformTypeIdentifiers
let uttypes = [String(kUTTypeEmailMessage)]
struct ContentView: View
{
let dropDelegate = EmailDropDelegate()
var body: some View
{
VStack
{
Text("Drag your email here!")
.padding(20)
}
.onDrop(of: uttypes, delegate: dropDelegate)
}
}
struct EmailDropDelegate: DropDelegate
{
func validateDrop(info: DropInfo) -> Bool
{
return true
}
func dropEntered(info: DropInfo)
{
print ("Drop Entered")
}
func performDrop(info: DropInfo) -> Bool
{
let items = info.itemProviders(for: uttypes)
for item in items
{
print (item.registeredTypeIdentifiers) // prints []
item.loadDataRepresentation(forTypeIdentifier: kUTTypeEmailMessage as String, completionHandler: { (data, error) in
if let data = data
{
print(data)
}
})
}
return true
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I'm not getting any data back that I can decode.
2020-11-08 09:34:54.877532+0000 DropContact[3856:124769] Cannot find representation conforming to type public.email-message
This feature has been eluding me forever so any help would be very much appreciated.
Well... the approach is the same, the only thing is that Apple Mail does not provide kUTTypeEmailMessage UTI representation on drag (copy)
If we register self for generic kUTTypeContent UTI and investigate content of pasteboard on drop mail from Mail, we get:
Ie, here is a complete list of representations:
com.apple.mail.PasteboardTypeMessageTransfer,
com.apple.mail.PasteboardTypeAutomator,
com.apple.pasteboard.promised-file-url,
dyn.ah62d4rv4gu8y6y4usm1044pxqzb085xyqz1hk64uqm10c6xenv61a3k,
NSPromiseContentsPboardType,
com.apple.pasteboard.promised-file-content-type,
dyn.ah62d4rv4gu8yc6durvwwa3xmrvw1gkdusm1044pxqyuha2pxsvw0e55bsmwca7d3sbwu,
Apple files promise pasteboard type,
public.url,
CorePasteboardFlavorType 0x75726C20,
dyn.ah62d4rv4gu8yc6durvwwaznwmuuha2pxsvw0e55bsmwca7d3sbwu,
Apple URL pasteboard type,
public.url-name,
CorePasteboardFlavorType 0x75726C6E,
public.utf8-plain-text,
NSStringPboardType
so now you can load data of any of those types from above (except of course Apple's own privates). And, by the way, that list might (and rather will) depend on macOS version.
I have no solution for this but might be on a path to it.
As I mentioned in a comment, it looks to me that SwiftUI does not have a way to fulfil file promises yet.
Afaik Mail, Photos and Safari using file promises while dragging images or mails.
This might help some one else with a solution.
func performDrop(info: DropInfo) -> Bool {
let pasteboard = NSPasteboard(name: .drag)
guard let items = pasteboard.pasteboardItems else { return false }
// it returns the content of the public.url → is an encoded message url
print(pasteboard.readObjects(forClasses: [NSURL.self]))
// getting available types for pasteboard
let types = NSFilePromiseReceiver.readableDraggedTypes.map { NSPasteboard.PasteboardType($0) }
for type in types {
for item in items {
print("type:", type)
print(item.data(forType: type))
}
}
}
This prints this:
Optional([message:%3Cf6304df5.BAAAA6Ge1-UAAAAAAAAAALTyTrMAAVNI2qYAAAAAAAcXzQBjqtkK#mailjet.com%3E])
type: NSPasteboardType(_rawValue: com.apple.NSFilePromiseItemMetaData)
nil
type: NSPasteboardType(_rawValue: dyn.ah62d4rv4gu8yc6durvwwa3xmrvw1gkdusm1044pxqyuha2pxsvw0e55bsmwca7d3sbwu)
nil
type: NSPasteboardType(_rawValue: com.apple.pasteboard.promised-file-content-type)
Optional(20 bytes)
But I don't know what data type com.apple.pasteboard.promised-file-content-type is … Maybe some kind of NSFilePromiseReceiver.
Maybe this helps someone.
Edit
Tried something else, maybe something more promising.
func performDrop(info: DropInfo) -> Bool {
let pasteboard = NSPasteboard(name: .drag)
guard let filePromises = pasteboard.readObjects(forClasses: [NSFilePromiseReceiver.self], options: nil) else { return false }
guard let receiver = filePromises.first as? NSFilePromiseReceiver else { return false }
let queue = OperationQueue.main
receiver.receivePromisedFiles(atDestination: URL.temporaryDirectory, operationQueue: queue) { (url, error) in
print(url, error)
}
}
In this example I'm able to get the NSFilePromiseReceiver but something is off.
After around 20 to 30s the callback is called and I get the URL finally – so there is maybe some potential to improve. I think it has something todo with the Queue.
Edit
I was able to move/copy the dropped mail into the download folder.
func performDrop(info: DropInfo) -> Bool {
let pasteboard = NSPasteboard(name: .drag)
guard let filePromises = pasteboard.readObjects(forClasses: [NSFilePromiseReceiver.self], options: nil) else { return false }
guard let receiver = filePromises.first as? NSFilePromiseReceiver else { return false }
let dispatchGroup = DispatchGroup()
let queue = OperationQueue()
let destUrl = URL.downloadsDirectory
dispatchGroup.enter()
var urls: [URL] = []
receiver.receivePromisedFiles(atDestination: destUrl, operationQueue: queue) { (url, error) in
if let error = error {
print(error)
} else {
urls.append(url)
}
print(receiver.fileNames, receiver.fileTypes)
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main, execute: {
print(urls)
})
}

How to make a CompletionHandler with Firebase Swift 3

I need to get some informations from Firebase to out it on UIViewCell. But the problem is that the function return before he get all the value. I know that's because it's asynchronous, i tried to make a completion Handler like that:
static func hardProcessingWithString(input: String, completion: #escaping (_ result: String) -> Void) {
Database.database().reference().child("Player_1").observe(.value) {
(firDataSnapshot) in
completion((firDataSnapshot.value as? String)!)
}
}
This is how i try to get the value :
var myVar: String!
hardProcessingWithString(input: "commands"){
(result: String) in
myVar = result
}
I get this error when I call the function:
Could not cast value of type '__NSDictionaryM' (0x102e292b0) to 'NSString' (0x102434c60).
Here's my Firebase database:
Or if you know how to make a Promise with Firebase let me know!
Step 1:
Create a swift file like Constant.swift. Then place the below code inside it
typealias DownloadComplete = () -> ()
Step 2:
Create a function where you want.
func hardProcessingWithString(completed: #escaping DownloadComplete) {
Database.database().reference().child("Player_1").observe (.value, with: { (firDataSnapshot) in
// put your code here
completed()
})
}
Step 3:
when you call above method in your code
hardProcessingWithString() {
// you can get the result here
}

Swift 3.0 NSFetchRequest error [duplicate]

In Swift 2 the following code was working:
let request = NSFetchRequest(entityName: String)
but in Swift 3 it gives error:
Generic parameter "ResultType" could not be inferred
because NSFetchRequest is now a generic type. In their documents they wrote this:
let request: NSFetchRequest<Animal> = Animal.fetchRequest
so if my result class is for example Level how should I request correctly?
Because this not working:
let request: NSFetchRequest<Level> = Level.fetchRequest
let request: NSFetchRequest<NSFetchRequestResult> = Level.fetchRequest()
or
let request: NSFetchRequest<Level> = Level.fetchRequest()
depending which version you want.
You have to specify the generic type because otherwise the method call is ambiguous.
The first version is defined for NSManagedObject, the second version is generated automatically for every object using an extension, e.g:
extension Level {
#nonobjc class func fetchRequest() -> NSFetchRequest<Level> {
return NSFetchRequest<Level>(entityName: "Level");
}
#NSManaged var timeStamp: NSDate?
}
The whole point is to remove the usage of String constants.
I think i got it working by doing this:
let request:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Level")
at least it saves and loads data from DataBase.
But it feels like it is not a proper solution, but it works for now.
The simplest structure I found that works in 3.0 is as follows:
let request = NSFetchRequest<Country>(entityName: "Country")
where the data entity Type is Country.
When trying to create a Core Data BatchDeleteRequest, however, I found that this definition does not work and it seems that you'll need to go with the form:
let request: NSFetchRequest<NSFetchRequestResult> = Country.fetchRequest()
even though the ManagedObject and FetchRequestResult formats are supposed to be equivalent.
Here are some generic CoreData methods that might answer your question:
import Foundation
import Cocoa
func addRecord<T: NSManagedObject>(_ type : T.Type) -> T
{
let entityName = T.description()
let context = app.managedObjectContext
let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)
let record = T(entity: entity!, insertInto: context)
return record
}
func recordsInTable<T: NSManagedObject>(_ type : T.Type) -> Int
{
let recs = allRecords(T.self)
return recs.count
}
func allRecords<T: NSManagedObject>(_ type : T.Type, sort: NSSortDescriptor? = nil) -> [T]
{
let context = app.managedObjectContext
let request = T.fetchRequest()
do
{
let results = try context.fetch(request)
return results as! [T]
}
catch
{
print("Error with request: \(error)")
return []
}
}
func query<T: NSManagedObject>(_ type : T.Type, search: NSPredicate?, sort: NSSortDescriptor? = nil, multiSort: [NSSortDescriptor]? = nil) -> [T]
{
let context = app.managedObjectContext
let request = T.fetchRequest()
if let predicate = search
{
request.predicate = predicate
}
if let sortDescriptors = multiSort
{
request.sortDescriptors = sortDescriptors
}
else if let sortDescriptor = sort
{
request.sortDescriptors = [sortDescriptor]
}
do
{
let results = try context.fetch(request)
return results as! [T]
}
catch
{
print("Error with request: \(error)")
return []
}
}
func deleteRecord(_ object: NSManagedObject)
{
let context = app.managedObjectContext
context.delete(object)
}
func deleteRecords<T: NSManagedObject>(_ type : T.Type, search: NSPredicate? = nil)
{
let context = app.managedObjectContext
let results = query(T.self, search: search)
for record in results
{
context.delete(record)
}
}
func saveDatabase()
{
let context = app.managedObjectContext
do
{
try context.save()
}
catch
{
print("Error saving database: \(error)")
}
}
Assuming that there is a NSManagedObject setup for Contact like this:
class Contact: NSManagedObject
{
#NSManaged var contactNo: Int
#NSManaged var contactName: String
}
These methods can be used in the following way:
let name = "John Appleseed"
let newContact = addRecord(Contact.self)
newContact.contactNo = 1
newContact.contactName = name
let contacts = query(Contact.self, search: NSPredicate(format: "contactName == %#", name))
for contact in contacts
{
print ("Contact name = \(contact.contactName), no = \(contact.contactNo)")
}
deleteRecords(Contact.self, search: NSPredicate(format: "contactName == %#", name))
recs = recordsInTable(Contact.self)
print ("Contacts table has \(recs) records")
saveDatabase()
This is the simplest way to migrate to Swift 3.0, just add <Country>
(tested and worked)
let request = NSFetchRequest<Country>(entityName: "Country")
Swift 3.0 This should work.
let request: NSFetchRequest<NSFetchRequestResult> = NSManagedObject.fetchRequest()
request.entity = entityDescription(context)
request.predicate = predicate
I also had "ResultType" could not be inferred errors. They cleared once I rebuilt the data model setting each entity's Codegen to "Class Definition". I did a brief writeup with step by step instructions here:
Looking for a clear tutorial on the revised NSPersistentContainer in Xcode 8 with Swift 3
By "rebuilt" I mean that I created a new model file with new entries and attributes. A little tedious, but it worked!
What worked best for me so far was:
let request = Level.fetchRequest() as! NSFetchRequest<Level>
I had the same issue and I solved it with the following steps:
Select your xcdatamodeld file and go to the Data Model Inspector
Select your first Entity and go to Section class
Make sure that Codegen "Class Definition" is selected.
Remove all your generated Entity files. You don't need them anymore.
After doing that I had to remove/rewrite all occurences of fetchRequest as XCode seem to somehow mix up with the codegenerated version.
HTH
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func loadItemsCategory() {
let request: NSFetchRequest<Category> = Category.fetchRequest()
do {
categoryArray = try context.fetch(request)
} catch {
print(error)
}
tableView.reloadData()
}

iOS DynamoDB Object Mapper load does not return all the attributes

I'm running below function in my iPad app to get an Item by it's hash key (IdName).
This table only contains an Hashkey (No Range key available) but when I run this, It returns an result object which contains only the HashKey Value (The same which I pass). The other property (IdValue) is not there in the result. What am I doing wrong here?
func getCurrentFinalReportNumber()->Int
{
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
var currentId :Int = -1
dynamoDBObjectMapper .load(DDBIDStoreTableRow.self, hashKey: "FinalReportNumber", rangeKey: nil) .continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (task:AWSTask!) -> AnyObject! in
if (task.error == nil) {
if (task.result != nil) {
let resultRow :DDBIDStoreTableRow = task.result as! DDBIDStoreTableRow
print(resultRow.IdValue)
currentId = Int(resultRow.IdValue!)
}
} else {
print("Error: \(task.error)")
let alert = SCLAlertView()
alert.addButton("Close", colorButtonBackground: Constants.InspectionTypes.colorClose) {}
alert.showCloseButton = false
alert.showError("", subTitle: (task.error?.description)!)
}
return nil
}).waitUntilFinished()
return currentId
}
class DDBIDStoreTableRow :AWSDynamoDBObjectModel ,AWSDynamoDBModeling {
var IdName:String? //HK
var IdValue:Int? = -1
class func dynamoDBTableName() -> String! {
return AWSIDStoreDynamoDBTableName
}
class func hashKeyAttribute() -> String! {
return "IdName"
}
//MARK: NSObjectProtocol hack
override func isEqual(object: AnyObject?) -> Bool {
return super.isEqual(object)
}
override func `self`() -> Self {
return self
}
}
Just found the mistake. Problem is in the data type of the mapping class. Earlier it was
var IdValue:Int? = -1
But once I change the Int to NSNumber as below. It started to work fine
var IdValue:NSNumber? = -1

How to get the correct callback value for NSUndoManager in Swift?

I have an NSDocument subclass, hooked up to an NSArrayController. For reference, I'm trying to translate the example from chapter 9 of Cocoa Programming for Mac OS X Fourth Edition.
It seems that from this question that I asked before, I need to use object-based undo with NSUndoManager. In order to pass two values to the method being invoked, I'm packaging them into an NSObject subclass with two instance variables.
When the KVO methods for inserting and deleting from the employees array are called by clicking on the buttons in my application, they work as expected.
However, when removeObjectFromEmployeesAtIndex is called during an undo operation, the index passed in is wildly out of bounds (for the first row, it always seems to be 55, then after that the index increases into the thousands for the next few rows).
How can I get the correct index to perform the undo action?
class Document: NSDocument {
var employee_list: Array<Person> = []
var employees: Array<Person> {
get {
return self.employee_list
}
set {
if newValue == self.employee_list {
return
}
self.employee_list = newValue
}
}
func insertObject(person: Person, inEmployeesAtIndex index: Int) {
self.undoManager.registerUndoWithTarget(self, selector: Selector("removeObjectFromEmployeesAtIndex:"), object: index)
if (!self.undoManager.undoing) {
self.undoManager.setActionName("Add Person")
}
employees.insert(person, atIndex: index)
}
func removeObjectFromEmployeesAtIndex(index: Int) {
let person = self.employees[index]
let pair = PersonIndexPair(person: person, index: index)
self.undoManager.registerUndoWithTarget(self, selector: Selector("insertPersonIndexPair:"), object: pair)
if (!self.undoManager.undoing) {
self.undoManager.setActionName("Remove Person")
}
employees.removeAtIndex(index)
}
func insertPersonIndexPair(pair: PersonIndexPair) {
insertObject(pair.person, inEmployeesAtIndex: pair.index)
}
}
Edit: I've worked around the issue by passing a string, but this seems pretty obtuse:
self.undoManager.registerUndoWithTarget(self, selector: Selector("removeObjectFromEmployeesAtStringIndex:"), object: String(index))
//...
func removeObjectFromEmployeesAtStringIndex(index: String) {
if let i = index.toInt() {
removeObjectFromEmployeesAtIndex(i)
}
}
Use NSNumber instead of Int.
self.undoManager.registerUndoWithTarget(self, selector:Selector("removeObjectFromEmployeesAtStringIndex:"), object: NSNumber(integer: index))
//...
func removeObjectFromEmployeesAtStringIndex(index: NSNumber) {
removeObjectFromEmployeesAtIndex(index.integerValue)
}
I am reading Cocoa Programming for Mac OS X now. I translate the sample Object-C code as below, only need to append it to class Document:
func insertObject(p: Person, inEmployeesAtIndex index: Int) {
//NSLog("adding %# to %#", p, employees)
let undo = self.undoManager
undo!.prepareWithInvocationTarget(self).removeObjectFromEmployeesAtIndex(index)
if !undo!.undoing {
undo!.setActionName("Add Person")
}
employees.insertObject(p, atIndex: index)
}
func removeObjectFromEmployeesAtIndex(index: Int) {
let p = employees.objectAtIndex(index) as! Person
//NSLog("Removing %# from %#", p, index)
let undo = self.undoManager
undo!.prepareWithInvocationTarget(self).insertObject(p, inEmployeesAtIndex: index)
if !undo!.undoing {
undo!.setActionName("Remove Person")
}
employees.removeObjectAtIndex(index)
}
I think it's cleanest to replace the old-fashioned Objective-C NSUndoManager method entirely:
private class SwiftUndoPerformer: NSObject {
let closure: Void -> Void
init(closure: Void -> Void) {
self.closure = closure
}
#objc func performWithSelf(retainedSelf: SwiftUndoPerformer) {
closure()
}
}
extension NSUndoManager {
func registerUndo(closure: Void -> Void) {
let performer = SwiftUndoPerformer(closure: closure)
registerUndoWithTarget(performer, selector: Selector("performWithSelf:"), object: performer)
//(Passes unnecessary object to get undo manager to retain SwiftUndoPerformer)
}
}
Then you can Swift-ly register any closure, and not worry about wrapping things up to get around an outdated interface:
undoManager.registerUndo {
self.insertObject(person, inEmployeesAtIndex: index)
}

Resources