Async tableview update in swift - swift

What if i put my updater in viewWillAppear using dispatch_async? I don't know... it seems too simple. I can't understand this because in c# after my threads i used .Abort(). What about swift? Will my thread aborted after runtime?
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
self.data = []
//some code with update
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}

I would recommend something along these lines:
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
updateData()
}
func updateData() {
makeAsyncCall{ (data) -> () in
dispatch_async(dispatch_get_main_queue()) {
self.data = []
self.tableView.reloadData()
}
}
}
func makeAsyncCall(completion: ([DataType]) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
/* do data update */
completion(data)
}
}
It better breaks up the functionality, and as far as I know (untested) it shouldn't have any issues.

Related

Swift - Dispatch Queues and the U/I running serially

I have a secondary LaunchScreenViewController for an App that has some animation whilst it gathers three types of background data.
Everything works but the order in which the DispatchQueues.async are run is random. However if I change them to DispatchQueues.sync everything happens in the right order but runs so fast (even with sleeps) you don't see the animations.
This needs to be .sync but how do I control the U/I so that I can see the animation? (Shown here as, e.g. self.subLogo1View.isHidden = true)
Here's the code:
// Queuing Variables
var semaphore = DispatchSemaphore(value: 1)
var semaphoreSub = DispatchSemaphore(value: 1)
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async {
self.semaphore.wait()
self.gatherData()
self.semaphore.signal()
}
DispatchQueue.global().async {
self.semaphore.wait()
self.checkNetworkAvailability()
self.semaphore.signal()
}
DispatchQueue.global().async {
self.semaphore.wait()
self.checkSomething()
self.semaphore.signal()
}
}
func gatherData() {
DispatchQueue.main.async {
self.semaphoreSub.wait()
print ("1")
self.subLogo1View.isHidden = true
self.subLogo1View.setNeedsDisplay()
self.semaphoreSub.signal()
}
}
func checkNetworkAvailability() {
DispatchQueue.main.async {
self.semaphoreSub.wait()
print ("2")
self.subLogo2View.isHidden = true
self.subLogo2View.setNeedsDisplay()
self.semaphoreSub.signal()
}
}
func checkSomething() {
DispatchQueue.main.async {
self.semaphoreSub.wait()
print ("3")
self.subLogo3View.isHidden = true
self.subLogo3View.setNeedsDisplay()
self.semaphoreSub.signal()
}
}
Instead of manually serializing your closures with a bunch of semaphores, you maybe better use a custom serial queue. For animation, user UIView.animate
Something like this:
func gatherData() {
DispatchQueue.main.async { // or sync, depending on your animation needs
print ("1: gather Data")
UIView.animate(withDuration: 0.5) {
self.subLogo1View.alpha = 0 // instead of isHidden
}
}
}
func viewDidLoad() {
var mySerialQueue = DispatchQueue (label:"my.serial")
mySerialQueue.async {
self.gatherData()
}
mySerialQueue.async {
self.checkNetworkAvailability()
}
// ...
}

How to showing line one by one in async code?

I want to do for-loop code with async. For simulating the async, I added some delays.
import Cocoa
class ViewController: NSViewController {
private let semaphore = DispatchSemaphore(value: 0)
private let concurrentQueue = DispatchQueue.global()
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...10 {
if run(i) {
break
}
}
}
func run(_ i:Int) -> Bool{
let seed = arc4random()
let isSuccess = seed % 5 == 1
let delayInSeconds = Int(seed % 3)
concurrentQueue.asyncAfter(wallDeadline: .now() + .seconds(delayInSeconds)) {
DispatchQueue.main.async {
self.textView.string += "\(i)\t\(seed)\n"
}
self.semaphore.signal()
}
semaphore.wait()
return isSuccess
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBOutlet var textView: NSTextView!
}
The problem is, when the code runs, all lines were added at once. I want it added one by one. Any idea?
The problem is solved. The reason that update UI part doesn't work is because that func run() is running on main thread, which is blocked by semaphore.wait() always. So to solve the problem, just added it to another thread.
override func viewDidLoad() {
super.viewDidLoad()
concurrentQueue.async {
for i in 1...10 {
if self.run(i) {
break
}
}
}
}

Async Download of Information from server

I want to download data from my server to be displayed on a map. Therefore I use async methods to get the data. The goal is to have an array of annotation objects to be displayed.
Therefore I first download Information A and then Information B. As both are async methods, I guess I need to wait for the completionHandler to return true so I know the data is loaded. This is easy for one method. But how do I handle to wait for both methods before the completionHandler of getInformationFromServer returns true and triggers therefore the addition of annotations?
override func viewWillAppear(animated: Bool) {
self.customizeInterface()
self.getInformationFromServer { (completed) -> Void in
if(completed) {
self.mapView.addAnnotations(self.annotationArray)
}
}
}
func getInformationFromServer(completionHandler: (completed: Bool) -> Void) {
getInformationFromServerA { (downloadCompleted) -> Void in
completionHandler(completed: downloadCompleted)
}
// HOW DO I MANAGE TO ONLY RETURN THE COMPLETION HANDLER TRUE WHEN
// BOTH FUNCTIONS RETURNED TRUE?
}
func getInformationFromServerA(completionHandler: (downloadCompleted: Bool) -> Void) {
Server().getJsonInformationFromServer(url: "aeds", completionHandler: { (response) -> Void in
self.parseAEDInformationToAnnotation(response["data"])
completionHandler(downloadCompleted: true)
})
}
func getInformationFromServerB(completionHandler: (downloadCompleted: Bool) -> Void) {
Server().getJsonInformationFromServer(url: "aeds", completionHandler: { (response) -> Void in
self.parseAEDInformationToAnnotation(response["data"])
completionHandler(downloadCompleted: true)
})
}
You may use a dispatch group to wait until both downloads finish.
func getInformationFromServer(completionHandler: (completed: Bool) -> Void) {
let dispatchGroup = dispatch_group_create()
var downloadCompletedA: Bool = false
dispatch_group_enter(dispatchGroup)
getInformationFromServerA { (downloadCompleted) -> Void in
downloadCompletedA = downloadCompleted
dispatch_group_leave(dispatchGroup)
}
var downloadCompletedB: Bool = false
dispatch_group_enter(dispatchGroup)
getInformationFromServerB { (downloadCompleted) -> Void in
downloadCompletedB = downloadCompleted
dispatch_group_leave(dispatchGroup)
}
// wait until both downloads are finished
dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER)
completionHandler(downloadCompletedA && downloadCompletedB)
}
See Apple's Concurrency Programming Guide:
Dispatch groups are a way to block a thread until one or more tasks
finish executing. You can use this behavior in places where you cannot
make progress until all of the specified tasks are complete.
Another solution which I can recommend for you and it's not so "complex" as solution with dispatch_group_wait:
func getInformationFromServer(completionHandler: (completed: Bool) -> Void) {
getInformationFromServerA { [weak self] (downloadCompleted: Bool) -> Void in
if downloadCompleted {
self?.getInformationFromServerB({ (downloadCompleted: Bool) -> Void in
completionHandler(completed: downloadCompleted)
})
}
else {
completionHandler(completed: downloadCompleted)
}
}
}

Given login/signUp code by Parse.com, has binary operator error

I am making SignUp/SignIn pages with watching youtube video.
He makes these pages with parseUI,
In his video, there is no error, but I get an error which is
"binary operator "|" cannot be applied to two 'PFLoginFields' operands" (It is checked where in my code down there. )
I checked in parse.com, parse example code is exactly same code with his.
So my Question is
How can I fix this code to work properly?
or Is there any other Binary operator I can use instead of ||?
import UIKit
import Parse
import ParseUI
class NewResisterVC: UIViewController, PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate {
var logInViewController : PFLogInViewController = PFLogInViewController()
var signUpViewController : PFSignUpViewController = PFSignUpViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if (PFUser.currentUser() == nil) {
/////////// HERE IS A PROBELM /////////////////
self.logInViewController.fields = (PFLogInFields.UsernameAndPassword | PFLogInFields.LogInButton | PFLogInFields.SignUpButton | PFLogInFields.PasswordForgotten | PFLogInFields.DismissButton)
//////////////////////////////////////////
var loginLogoTitle = UILabel()
loginLogoTitle.text = "bany"
self.logInViewController.logInView!.logo = loginLogoTitle
self.logInViewController.delegate = self
var signUpLogoTitle = UILabel()
signUpLogoTitle.text = "bany"
self.signUpViewController.signUpView!.logo = signUpLogoTitle
self.signUpViewController.delegate = self
self.logInViewController.signUpController = self.signUpViewController
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: Parse Login
func logInViewController(logInController: PFLogInViewController, shouldBeginLogInWithUsername username: String, password: String) -> Bool {
if(!username.isEmpty || !password.isEmpty) {
return true
}else{
return false
}
}
func logInViewController(logInController: PFLogInViewController, didLogInUser user: PFUser) {
self.dismissViewControllerAnimated(true, completion: nil)
}
func logInViewController(logInController: PFLogInViewController, didFailToLogInWithError error: NSError?) {
print("Fail to login")
}
//MARK: Parse Sign Up
func signUpViewController(signUpController: PFSignUpViewController, didSignUpUser user: PFUser) {
self.dismissViewControllerAnimated(true, completion: nil)
}
func signUpViewController(signUpController: PFSignUpViewController, didFailToSignUpWithError error: NSError?) {
print("fail to sign up...")
}
func signUpViewControllerDidCancelSignUp(signUpController: PFSignUpViewController) {
print("User dismissed sign up")
}
// mark: Actions
#IBAction func simpleAction(send: AnyObject) {
self.presentViewController(self.logInViewController, animated: true, completion: nil)
}
}
The guy in video is using xcode 6 with swift 1.2, you are using xcode 7 with swift 2.0. Syntax has changed, you have to rewrite this line like this:
self.logInViewController.fields = [PFLogInFields.UsernameAndPassword,
PFLogInFields.LogInButton, PFLogInFields.SignUpButton,
PFLogInFields.PasswordForgotten, PFLogInFields.DismissButton]
Also note that you don't need to write typename prefix when assigning to variable:
self.logInViewController.fields = [.UsernameAndPassword, .LogInButton,
.SignUpButton, .PasswordForgotten, .DismissButton]

Wait for completion before executing next step

I have a few processes that need to be completed in order when my TableView loads. I would expect that it would wait until the code completed before executing the next line of code, but it seems that's not the case. Is there a way to have these wait until completion before executing the next step?
override func viewDidLoad() {
super.viewDidLoad()
performTask1()
performTask2()
performTask3()
}
Thanks for all the help!
The typical example to make each of these methods take a completionHandler parameter, e.g.:
func perform1(completionHandler: () -> Void) {
doSomethingAsynchronously() {
completionHandler()
}
}
func perform2(completionHandler: () -> Void) {
doSomethingElseAsynchronously() {
completionHandler()
}
}
func perform3(completionHandler: () -> Void) {
doSomethingCompletelyDifferentAsynchronously() {
completionHandler()
}
}
Then you can run them like so:
override func viewDidLoad() {
super.viewDidLoad()
perform1 {
self.perform2 {
self.perform3 {
}
}
}
}