Swift (IOS) Update parameter in async function - swift

I'm currently working on creating a framework for IOS. My framework has to send request to my server, parse the response and then update the UITableView of the main page of the App.
I've already create a Singleton class "MyUpdater" which has a static func "addRow"
In my Application i have 2 objects: the tableView and a list of objects that i use for the tableView.
I want the "addRow" method (of my FrameWork) to add an object to the object list of the App asynchronously.
So i created the following method:
public static func addRow(tableView: UITableView, list: inout Array<Product>){
var my_object = Object()
list.insert(my_object, at: 1)
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: 1, section: 0)], with: .automatic)
tableView.reloadData()
tableView.enUpdates()}
And i get the following error: escaping closures can only capture inout.
How can i asynchronously update the list of the Application from the pod (framework) ?
Thank you.
Kind regards

why are you using inout ? I think this makes no sense here, since inout isn't a pass-by-reference.
Did you try something like: ?
public func addRow(tableView: UITableView, list: Array<Product>){
Dispatch.main.async {
//update the list + tableview
}
}

Related

How would you implement multiple items using NSOUtlineViews's pasteboardWriterForItem:

I am using an NSOutlineView with drag and drop support. There is an NSOutlineViewDataSource function entitled outlineView(_:pasteboardWriterForItem:).
This call serializes the item passed into the function and allows the user to retrieve the serialized item in outlineView(_:acceptDrop:item:childIndex:)
#MainActor optional func outlineView(
_ outlineView: NSOutlineView,
acceptDrop info: NSDraggingInfo,
item: Any?,
childIndex index: Int
) -> Bool
That being said, I don't understand how I would write multiple rows of data using outlineView(_:pasteboardWriterForItem:) . It gets called for each item being moved, but the pasteboard only keeps a reference to the last item. Apple deprecated several multiple row write/move functions.
How should I implement this? I have tried several solutions but they're hacky and seem like they're not the canonical solution.
The information is in the info parameter.
There is an API enumerateDraggingItems(options:for:classes:searchOptions:using:) of NSDraggingInfo to enumerate and proceed the items.
In outlineView(_:pasteboardWriterForItem:) you usually just
return item as? NSPasteboardWriting

How to get a user attribute from an AWS Cognito user pool in Swift?

I'm using AWS Cognito user pools with Amazon's Swift sample app. I'm able to create users with a given_name attribute, but it's not obvious how to later retrieve the given_name.
The Amazon sample retrieves attributes as a AWSCognitoIdentityUserGetDetailsResponse and then dumps them to the screen. However, I can't find documentation for AWSCognitoIdentityUserGetDetailsResponse. It appears to be something of an array, but it's not obvious to me how to just pull out a given_name from the returned attributes. One would think that returning attributes as a dictionary would be a good idea, but it doesn't appear that Amazon did things that way.
Any pointers?
EDIT: To clarify, what's returned is an array of AttributeType objects. Here's code in the Cognito sample which displays all returned attributes:
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "attribute", for: indexPath)
let userAttribute = self.response?.userAttributes![indexPath.row]
cell.textLabel!.text = userAttribute?.name
cell.detailTextLabel!.text = userAttribute?.value
return cell
}
Here's the raw response:
Response body:
{"UserAttributes":[{"Name":"sub","Value":"XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX"},{"Name":"email_verified","Value":"true"},
{"Name":"given_name","Value":"Bob"},
{"Name":"email","Value":"bob#example.com"}],"Username":"AAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"}
It's just not obvious to me how to pull out given_name without iterating through the whole array.
Not an iOS expert here, but from what I can see in the SDK implementation, it looks like they copy the details from the AWSCognitoIdentityProviderGetUserResponse, which documentation shows it has the user attributes in the form of a map. Did you try to look for an userAttributes array in the response?
Also, the raw GetUser API says that the UserAttributes should be in the response.
Here's an example of using getDetails() to access userAttributes
self.user?.getDetails().continueOnSuccessWith { (task) -> AnyObject? in // handle all auth setup
DispatchQueue.main.async(execute: {
self.response = task.result // AWSCognitoIdentityUserGetDetailsResponse
if let attributes = task.result?.userAttributes { // https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html
for attribute in attributes {
print(attribute.name, attribute.value)
if attribute.name == "name" {
// ... do something with name
}
}
}
})
return task
}
}

tableView function runs before array is populated

I am trying to populate a Table View and I think my functions are running in the wrong order.
I have a simple function loadSampleAssignments() that populates an array. If I call that function directly in viewDidLoad my table populates correctly.
The problem is that I need to call the code after a successful response from Auth0 in this code:
fileprivate func retrieveProfile() {
guard let accessToken = loginCredentials.accessToken else { return }
Auth0
.authentication()
.userInfo(token: accessToken)
.start { result in
switch result {
case .success( _):
self.loadSampleAssignments()
case .failure( _): break
}
}
}
Unfortunately, when I call the function here my UI updates with an empty array. I can see that loadSampleAssignments() is called, but it is after my cellForRowAt function has updated the table cells.
Is there a way I can manually call the cellForRowAt function inside my loadSampleAssignments function?
I have been stuck for days, and would really appreciate any help on this! Thanks!
At the point where you're ready for the table to be repopulated, call tableView.reloadData(), which will trigger cellForRow. It is not a problem that cellForRow also gets triggered before the data is ready to display -- this is normal and doesn't mean you're doing anything wrong.

Cannot invoke function function with no arguments

I am still new to iOS programming and trying to understand the logic behind the program. I am trying to call a function inside of the tableView function. I just want a piece of code inside of the tableView to run at a certain point. For a simple example
override func viewDidLoad() {
tableView.testFunction()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
func testFunction() {
println("test")
}
...
}
But I get the error that I cannot invoke function with no arguments. I am new to swift so I apologize for the very basic question.
One function cannot call a function that is inside another function.
You have defined testFunction() as a local function inside your tableView:cellForRowAtIndexPath: function. No other function can see it; it is visible only inside tableView:cellForRowAtIndexPath: (and only to later code).
See my book:
A function declared in the body of a function (also called a local function) is available to be called by later code within the same scope, but is completely invisible outside its scope.

How do I dispatch functions in Swift the right way?

I've kept trying but I just don't get it. I'm rather new to programming so almost every new step is an experiment. Whereas I have no problems dispatching normal closures without arguments/returns, I haven't understood so far how to deal with functions that take (multiple) arguments and return in the end.
To get the logic of the proper "work around" it would be great if someone could post a practical example so I could see whether I've got all of it right. I'd be very thankful for any kind of help... If some other practical example illustrate the topic in a better way, please go ahead with your own!
Let's say we'd like to asynchronously dispatch the following function to a background queue with low priority (or do I make the mistake, trying to implement the dispatch when defining a function instead of waiting till it is called from somewhere else?!):
func mutateInt(someInt: Int) -> Int {
"someHeavyCalculations"
return result
}
or a function with multiple arguments that in addition calls the first function at some point (everything in background queue):
func someBadExample(someString: String, anotherInt: Int) -> Int {
"someHeavyStuff"
println(testString)
mutateInt(testInt)
return result
}
or a UI-function that should be ensured to run just on main queue (just a fictitious example):
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionInfo = self.fetchedResultsController.sections?[section] as NSFetchedResultsSectionInfo
return sectionInfo.numberOfObjects
}
Let's say you had some function like so:
func calculate(foo: String, bar: Int) -> Int {
// slow calculations performed here
return result
}
If you wanted to do that asynchronously, you could wrap it in something like this:
func calculate(foo: String, bar: Int, completionHandler: #escaping (Int) -> Void) {
DispatchQueue.global().async {
// slow calculations performed here
completionHandler(result)
}
}
Or, alternatively, if you want to ensure the completion handler is always called on the main queue, you could have this do that for you, too:
func calculate(foo: String, bar: Int, completionHandler: #escaping (Int) -> Void) {
DispatchQueue.global().async {
// slow calculations performed here
DispatchQueue.main.async {
completionHandler(result)
}
}
}
For the work being performed in the background, you may use a different priority background queue, or your might use your own custom queue or your own operation queue. But those details aren't really material to the question at hand.
What is relevant is that this function, itself, doesn't return any value even though the underlying synchronous function does. Instead, this asynchronous rendition is passing the value back via the completionHandler closure. Thus, you would use it like so:
calculate(foo: "life", bar: 42) { result in
// we can use the `result` here (e.g. update model or UI accordingly)
print("the result is = \(result)")
}
// but don't try to use `result` here, because we get here immediately, before
// the above slow, asynchronous process is done
(FYI, all of the above examples are Swift 3. For Swift 2.3 rendition, see previous version of this answer.)