Swift: Multipeer Connectivity + UI Update - ios

My application is to communicate between about 5 iPads and update according to the data received through multipeer connectivity framework.
One of the iPad do the followings: It get its current location and send it to other peers. The peers update the UI according to the location received.
I found that there is a problem that when the iPad need to send the packet to the others, it cause its UI did not update immediately. The UI events stuck until it finished sending the packet. Since the location keep on updated, all these events fired continuously.
I have tried to place the send packet in a thread:
let bgQueue = DispatchQueue(label: "hk.edu.polyu.isurf.sendqueue", qos: .utility, attributes: .concurrent)
And then put the code for sending packet inside this bgQueue:
func sendPacket {
bgQueue.async {
// create packet, and send
}
}
My location update code will cause this:
func receiveLocation() {
sendPacket()
updateUI()
}
How can I improve the efficiency? The UI basically cannot update now, it is of serious "lagging".
I have tried to change the type of the bgQueue, but no improvement.
Thank you.

You should make sure that your UI is being updated on the main queue.
If your trying to update UI in the session didRecieve function, this is a function that runs in the background. This means any UI changes made inside of it need to be put inside a Dispatch to the main queue so its updates take place immediately.
DispatchQueue.main.async
{
// UI updates go here
}

Related

Unable to run JSContext in background thread

Here I'm initiating the JS call from swift and based on the results app UI gets updated. Since this method is time taking process it freezes the app for a few seconds and becomes normal. We can understand it's running in the main thread so blocked the UI. I have tried to run it in the background using the below code and it doesn't work.
DispatchQueue.global(qos: .background).async {
let fn = engineJSContext?.objectForKeyedSubscript("jsMethodName")
let val = fn?.call(withArguments: [params1, params2])
}
As per the below document, we can run it from any thread, but I'm facing a problem on the background thread. Any help on this, please!
https://developer.apple.com/documentation/javascriptcore/jsvirtualmachine

GCD: URLSession download task

I have a requirement to download large number of files - previously only one file could be downloaded at a time. The current design is such that when the user downloads a single file, a URLSession task is created and the progress/completion/fail is recorded using the delegate methods for urlsession. My question is, how can I leave a dispatch group in this delegate method? I need to download 10 files at a time, start the next 10 when the previous ten finishes. Right now, if I leave the dispatch group in the delegate method, the dispatch group wait waits forever. Here's what I've implemented so far:
self.downloadAllDispatchQueue.async(execute: {
self.downloadAllDispatchGroup = DispatchGroup()
let maximumConcurrentDownloads: Int = 10
var concurrentDownloads = 0
for i in 0..<files.count
{
if self.cancelDownloadAll {
return
}
if concurrentDownloads >= maximumConcurrentDownloads{
self.downloadAllDispatchGroup.wait()
concurrentDownloads = 0
}
if let workVariantPart = libraryWorkVariantParts[i].workVariantPart {
concurrentDownloads += 1
self.downloadAllDispatchGroup.enter()
//call method for download
}
}
self.downloadAllDispatchGroup!.notify(queue: self.downloadAllDispatchQueue, execute: {
DispatchQueue.main.async {
}
})
})
In the delegates:
func downloadDidFinish(_ notification: Notification){
if let dispatchGroup = self.downloadAllDispatchGroup {
self.downloadAllDispatchQueue.async(execute: {
dispatchGroup.leave()
})
}
}
Is this even possible? If not, how can I achieve this?
If downloadAllDispatchQueue is a serial queue, the code in your question will deadlock. When you call wait, it blocks that current thread until it receives the leave call(s) from another thread. If you try to dispatch the leave to a serial queue that is already blocked with a wait call, it will deadlock.
The solution is to not dispatch the leave to the queue at all. There is no need for that. Just call it directly from the current thread:
func downloadDidFinish(_ notification: Notification) {
downloadAllDispatchGroup?.leave()
}
When downloading a large number of files, we often use a background session. See Downloading Files in the Background. We do this so downloads continue even after the user leaves the app.
When you start using background session, there is no need to introduce this “batches of ten” logic. The background session manages all of these requests for you. Layering on a “batches of ten” logic only introduces unnecessary complexities and inefficiencies.
Instead, we just instantiate a single background session and submit all of the requests, and let the background session manage the requests from there. It is simple, efficient, and offers the ability to continue downloads even after the user leaves the app. If you are downloading so many files that you feel like you need to manage them like this, it is just as likely that the end user will get tired of this process and may want to leave the app to do other things while the requests finish.

Why would you need a DispatchQueue when showing Alerts on Swift?

I am new to Swift, and trying to examine a finished project. But there is something i couldn't understand.
After a network request is completed, the app show an alert under a condition.
func makeNetworkRequest() {
//newtork result...
DispatchQueue.main.async {
self.showAlert(versionMessage: "Error")
}
}
func showAlert(versionMessage: String) {
let alert = UIAlertView(title: "", message: versionMessage, delegate: self)
alert.show()
}
However, it is done with a DispatchQueue. Why would anyone need to use DispatchQueue in this situation.
It’s a conscious design decision from Apple’s side to not have UIKit
be thread-safe. Making it thread-safe wouldn’t buy you much in terms
of performance; it would in fact make many things slower. And the fact
that UIKit is tied to the main thread makes it very easy to write
concurrent programs and use UIKit. All you have to do is make sure
that calls into UIKit are always made on the main thread. So
according to this the fact that UIKit objects must be accessed on
the main thread is a design decision by apple to favor performance.
for more detailed information you can go through this article
https://www.objc.io/issues/2-concurrency/thread-safe-class-design/
In your case , You are showing alert from another thread so you have to write code under the MainThread so , you can get the main thread using below code
DispatchQueue.main.async {
// Your UI Updation here
}
Reason
In Cocoa Touch, the UIApplication i.e. the instance of your application is attached to the main thread because this thread is created by UIApplicatioMain(), the entry point function of Cocoa Touch. It sets up main event loop, including the application’s run loop, and begins processing events. Application's main event loop receives all the UI events i.e. touch, gestures etc.
You´ll for sure notice that the alert will lag if you don´t show the alert on the main thread, that´s because your UI code does always have to be done on your main thread.
So if you're on a background thread and want to execute code on the main thread, you need to call async(). That´s way you call DispatchQueue.main, which is the main thread.

asynchronous Swift function taking long time to update label [duplicate]

I'm beginner swift developer. I'm stucked with this weather app.I'm downloading website data and then displaying in my label.
Unfortunately this whole process takes like 10 second to update my label.
This is probably not because of the network connection as the console is updated instantly.
Thanks for suggestions.
What happens is that code is probably run on a secondary thread. Any UI changes you make should be made on the main thread. So try this:
dispatch_async(dispatch_get_main_queue()) {
// update label
}
This should update your label instantly.
Previously, we would choose the dispatch method (sync vs async) and then the queue we wanted to dispatch our task to. The updated GCD reverses this order - we first choose the queue and then apply a dispatch method.
Swift 3:
Now in Swift 3 the GCD library was updated like in the following way:
DispatchQueue.main.async(execute: {
// UI Updates
})
I hope this help you.
It might be late to answer but in Swift 3 logic should be like this.
DispatchQueue.global(qos: .background).async {
// Background Thread Or Service call Or DB fetch etc
DispatchQueue.main.async {
// Run UI Updates and other logic
}}
Your code has multiple issues.
FIRST: It has strong reference cycle
Fix it by putting this in closure.
[weak weakSelf = self]
SECOND: Update UI in main thread
DispatchQueue.main.async {
//Update UI
weakSelf?.mReslut.text = ""
}

Updating label takes too long (swift)

I'm beginner swift developer. I'm stucked with this weather app.I'm downloading website data and then displaying in my label.
Unfortunately this whole process takes like 10 second to update my label.
This is probably not because of the network connection as the console is updated instantly.
Thanks for suggestions.
What happens is that code is probably run on a secondary thread. Any UI changes you make should be made on the main thread. So try this:
dispatch_async(dispatch_get_main_queue()) {
// update label
}
This should update your label instantly.
Previously, we would choose the dispatch method (sync vs async) and then the queue we wanted to dispatch our task to. The updated GCD reverses this order - we first choose the queue and then apply a dispatch method.
Swift 3:
Now in Swift 3 the GCD library was updated like in the following way:
DispatchQueue.main.async(execute: {
// UI Updates
})
I hope this help you.
It might be late to answer but in Swift 3 logic should be like this.
DispatchQueue.global(qos: .background).async {
// Background Thread Or Service call Or DB fetch etc
DispatchQueue.main.async {
// Run UI Updates and other logic
}}
Your code has multiple issues.
FIRST: It has strong reference cycle
Fix it by putting this in closure.
[weak weakSelf = self]
SECOND: Update UI in main thread
DispatchQueue.main.async {
//Update UI
weakSelf?.mReslut.text = ""
}

Resources