Why doesn't my completion block work? - ios

Implementing solution given here How to make a synchronous request using Alamofire?
I don't get any errors, it just doesn't work as expected. In tableViewController
override func viewDidLoad() {
super.viewDidLoad()
loadData() { (didCompleteRequest) in
if (didCompleteRequest) {
self.TodosTableView.delegate = self
self.TodosTableView.dataSource = self
print("loading successfull")
} else {
print("loading failed")
}
}
print("leaving viewDidLoad")
}
func loadData(completion: #escaping (Bool) -> Void) {
Alamofire.request(TodosViewController.serverAdress + "projects/index.json").responseJSON { response in
do {
// async stuff
} catch {
completion(false)
}
print("leaving loadData")
completion(true)
}
}
output I get
leaving viewDidLoad
leaving loadData
loading successfull
apparently, the first element should be the last one

First viewDidLoad is running in the main thread. So when you put this loadData() in viewDidLoad controls dispatched to background thread where alamofire works on, and the main thread continues and prints leaving viewDidLoad
Try this
override func viewDidLoad() {
super.viewDidLoad()
self.TodosTableView.delegate = self
self.TodosTableView.dataSource = self
loadData() { (didCompleteRequest) in
if (didCompleteRequest) {
self.TodosTableView.reloadData()
print("loading successfull")
} else {
print("loading failed")
}
}
print("leaving viewDidLoad")
}

You are call block code after get response from server. so first call "print("leaving viewDidLoad")".
response code get with delay so call block code with delay

Related

How to handle crashes caused by semaphore in iOS?

Crashes when the class is destructed, how to handle unfinished semaphore?
class CrashTestViewCtrl: UIViewController {
private var semaphore = DispatchSemaphore(value: 2)
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async { [weak self] in
self?.semaphore.wait()
// do something ......
}
}
deinit {
print("……deinit……")
}
}
It crashes because of lack of semaphore.signal() and wrong setup of semaphore.
You should call semaphore.signal() after you are done with the async method.
You should add semaphore.wait() outside of the async method.
In your case, it could be like this;
class CrashTestViewCtrl: UIViewController {
private var semaphore = DispatchSemaphore(value: 2)
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global().async { [weak self] in
// do something ......
self?.semaphore.signal()
}
semaphore.wait()
}
deinit {
print("……deinit……")
}
}

SwiftSpinner in initial load is not working

Upon calling SwiftSpinner, on viewDidLoad , Swift spinner is not working
Please find below the code i am using,
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global(qos: .background).async {
self.basicSync {(isSynced) in
DispatchQueue.main.async {
SwiftSpinner.hide()
}
}
}
DispatchQueue.main.async {
SwiftSpinner.show("Syncing")
}
}
public func basicSync(completion: #escaping (Bool) -> ()) {
//Sync related work
completion("true")
}
I am not able to find a way to call Swift spinner on initial load
Some of the methods i tried with SwiftSpinner.Show from above code
To call,
SwiftSpinner.show("Syncing")
To call it main inside main thread
DispatchQueue.main.async {
DispatchQueue.main.async {
SwiftSpinner.show("Syncing")
}
}
I was able to achieve it when it is written like this,
DispatchQueue.main.async {
DispatchQueue.main.async {
DispatchQueue.main.async {
SwiftSpinner.show("Syncing")
}
}
}
But I dont think it is the correct coding standard, new to Swift and coding technology
I have this issue before, you need to do the following
func delay(seconds: Double, completion: #escaping () -> ()) {
let popTime = DispatchTime.now() + Double(Int64( Double(NSEC_PER_SEC) * seconds )) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: popTime) {
completion()
}
}
then in view did load
override func viewDidLoad() {
super.viewDidLoad()
delay(seconds: 0.5) {
SwiftSpinner.show("Syncing")
}
}
That's it :D
Try adding it in
viewdidappear

Pull Refresh with Alamofire Swift 4

On my app, I am using Alamofire to implement pull to refresh, which is working to refresh but unfortunately doesn't bring back any data. What am I doing wrong? Something for sure I am missing.
Here is the code
my class EarthquakeListing
var earthquake: Earthquake?
var refresh = UIRefreshControl()
on the viewDidLoad()
// Refresh Control
//let refreshControl = UIRefreshControl()
refresh.tintColor = UIColor.yellow
refresh.addTarget(self, action: #selector(refreshData), for: UIControlEvents.valueChanged)
if #available(iOS 10.0, *) {
self.tableView.refreshControl = (self.refresh)
} else {
self.tableView.addSubview(self.refresh)
}
Alamofire on viewDidLoad()
Alamofire.request("http://www.seismicportal.eu/fdsnws/event/1/query?limit=50&format=json").responseJSON { response in
//print("Request: \(String(describing: response.request))") // original url
if let data = response.data{
do {
self.earthquake = try
JSONDecoder().decode(Earthquake.self, from: data)
print("features data: \(String(describing: self.earthquake?.features))")
// print("data: \(self.features.count)")
self.tableView.reloadData()
self.refresh.endRefreshing()
}
catch{}
}
}
}
and the func of objc
#objc func refreshData() {
self.tableView.reloadData()
self.refresh.endRefreshing()
}
Add your Alamofire request in a function and call it inside refreshData(). And for first time data loading you can call that function(containing Alamofire request) inside your viewDidLoad().
func myRequest() {
// Alamofire request goest here.....
/* Inside of Alamofire callback block */
// Assign data to data source
self.tableView.reloadData()
if (self?.refresh.isRefreshing)! {
self.refresh.endRefreshing()
}
/*--------*/
}
override func viewDidLoad() {
super.viewDidLoad()
myRequest()
}
#objc func refreshData() {
myRequest()
}
Update your method with the following code:
#objc func refreshData() {
// start refresh control
self.refresh.beginRefreshing()
// get data from server
Alamofire.request("http://www.seismicportal.eu/fdsnws/event/1/query?limit=50&format=json").responseJSON { response in
if let data = response.data {
do {
self.earthquake = try JSONDecoder().decode(Earthquake.self, from: data)
// End refreshing and reload table
self.refresh.endRefreshing()
self.tableView.reloadData()
}
catch{}
}
}
}

How to make function for HTTP request on other class in Swift?

I'm writing a app to make a http requests with SwiftHTTP, but I want to create a class to make this, and in the ViewController I call the functions to do this. So, I create a UIButton to call the functions.
I don't know if I need use threads or exists ways to make this easy.
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self,action:#selector(self.clickRequest(sender:)), for: UIControlEvents.touchUpInside )
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#objc func clickRequest (sender: UIButton) {
}
}
HTTPService.swift
import Foundation
import SwiftHTTP
class HTTPService {
}
How I make this in others Apps. But inside of ViewController.swift.
HTTP.GET("https://www.host.com/example",requestSerializer: JSONParameterSerializer()) {
response in
if let err = response.error {
print(err.localizedDescription)
return
}
} catch let err as NSError {
print(err)
}
}
Create one method with successBlock and failureBlock inside your HTTPService service class like below,
class HTTPService {
func makeRequest(params: [String: Any], successBlock: () -> Void, failureBlock: () -> Void) {
HTTP.GET("https://www.host.com/example",requestSerializer: JSONParameterSerializer()) {
response in
if let err = response.error {
print(err.localizedDescription)
failureBlock() // Call failure block
return
} else {
successBlock() // Call success block
}
} catch let err as NSError {
print(err)
failureBlock() // Call failure block
}
}
}
You can call this method like below,
#objc func clickRequest (sender: UIButton) {
HTTPService().makeRequest(params: ["name": "userName"], successBlock: {
// Handle API success here. E.g Reloading table view
}) {
// Handle API failure here. E.g Showing error to user
}
}
Thanks

Do one action when method is finished in Swift

I want do one action when one method is finished, I execute one method in other method and I want the second method stop until the first method is finished.
I have that method:
func ejecutarOBJC(){
let txtNombre = self.view.viewWithTag(4) as? UITextField
let textoNombre=txtNombre?.text
let txtContra = self.view.viewWithTag(5) as? UITextField
let textoContra=txtContra?.text
let instanceOfCustomObject: SQLViewController = SQLViewController()
instanceOfCustomObject.nombre = textoNombre;
instanceOfCustomObject.contra = textoContra;
instanceOfCustomObject.obtenerExistenciaUsuario()
}
And also the other method:
func otherMethod(){
ejecutarOBJC()
//I want continue with that method when the execution of the other method finish
}
This is how you would achieve this:
func methodOne() {
//Method one code here
methodTwo()
}
func methodTwo() {
//Method Two code here.
}
As per your comment, here is how to wait when using async code:
func methodOne() {
//Code goes here
methodTwo { () -> () in
//Method two has finished
}
}
func methodTwo(completion: () -> ()) {
//Code goes here
completion()
}
Use closures:
func callCompetionFunction()
{
// Tira mola meseda (do your stuff)
completionFunction("a parameter") { () -> Void in
print("function copleted")
}
}
func completionFunction(param: AnyObject, completion: ()->Void)
{
// Do your stuff
completion()
}
To test it in a view controller
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
callCompetionFunction()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func callCompetionFunction()
{
// Tira mola meseda (do your stuff)
print("do something here")
completionFunction("a parameter") { () -> Void in
print("function copleted")
}
}
func completionFunction(param: AnyObject, completion: ()->Void)
{
// Do your stuff
if param is String
{
print("parameter = \(param)")
}
print("going to execute completion closure")
completion()
}
}

Resources