Alert view controller not presented once API call successfully completed SWIFT - ios

I have the following API call:
func updateSheetVals() {
let args = ["id": viewModel.id]
let measurements = UpdateRequest.Measurements(
departure: Double(sheet.postRefuelQuantity.field.currentValue),
discrepancy: Double(sheet.discrepancy.field.currentValue),
discrepancyPercentage: Double(sheet.discrepancyPercent.field.currentValue),
preTotal: Double(dheet.preTotal.field.currentValue),
targetTotal: Double(fuelSheet.requiredTotal.field.currentValue)
)
let body = UpdateRequest(measurement: measurements)
API.client.post(.updateFuelSheetVals, with: args, using: .put, posting: body, expecting: MessageResponse.self) { (success, response) in
switch success {
case .failure:
print("Check network connection")
case .success:
DispatchQueue.asyncMain {
self.present(WarningViewController.finished(), animated: true)
}
}
}
}
}
And yet, even though I receive a 200 response and the API is called correctly, my view controller is never displayed. Happy to provide more contextual code if needed, but wondered firstly if I'm just missing something obvious with this block...
EDIT:
The API call is triggered in the following code:
func acceptButtonPressed(_ button: Button) {
var confirmation: UIViewController & ConfirmationAction
guard let level = viewModel.getSelectedSheet().order.levelDouble else { return }
if self.viewModel.requiresSignature {
if level < 3 {
confirmation = SignatureViewController(hasDiscrepancy: viewModel.hasDiscrepancy, discrepancyPrompt: viewModel.discrepancyPrompt, sl: level)
} else {
confirmation = SignatureViewController(hasDiscrepancy: viewModel.hasDiscrepancy,
discrepancyPrompt: viewModel.discrepancyPrompt, sl: level)
}
} else {
if let userInputAllowed = sheet.userInputAllowed, level < 3, !userInputAllowed {
confirmation = OrderAcceptAlertViewController.alert()
} else if level < 3 {
confirmation = DiscrepancyAlertViewController.alertWithDiscrepancy(hasDiscrepancy: viewModel.hasDiscrepancy,
discrepancyPrompt: viewModel.discrepancyFromManualInput(discrepancyValue: fuelSheet.percentageDiscrepancy.field.currentValue))
} else {
confirmation = DiscrepancyAlertViewController.alertWithDiscrepancy(hasDiscrepancy: viewModel.hasDiscrepancy,
discrepancyPrompt: viewModel.discrepancyPrompt)
}
}
confirmation.confirmationAction = { [weak confirmation, weak self] response in
guard let self = self else {
return
}
var completedSignature: SignatureParameter?
switch response {
case let .signature(signature):
completedSignature = signature
case .discrepancy:
break
}
let args = ["id": self.viewModel.id]
let params = AcceptParameter(
employee: self.viewModel.employee,
signature: completedSignature,
base64FuelSheet: self.sheet.ticket?.base64
)
if let confirm = confirmation {
confirm.setLoading(true)
API.client.post(.accept, with: args, using: .put, posting: params, expecting: Nothing.self, completion: { [weak self] (success, _) in
DispatchQueue.asyncMain {
guard let self = self else {
return
}
confirm.setLoading(false)
self.navigationController?.popViewController(animated: true)
}
}
})
self.updateSheetVals()
}
}
present(confirmation, animated: true, completion: nil)
}

For this
self.navigationController?.popViewController(animated: true)
to work the vc must be inside a navigation controller and if not this self.navigationController? will be nil and nothing will show , you either need a segue/push.present like
self.present(viewController, animated: true, completion: nil)

This will work for sure:
case .success:
DispatchQueue.asyncMain {
let viewController = self.storyboard?.instantiateViewController(withIdentifier: WarningViewController) as! WarningViewController
self.present(viewController, animated: true, completion: nil)
}
}

Related

TableView not reloading with firebase data after dismissing modal controller

After logging in with firebase Auth, I try to update the home page tableview using a delegate except I get this issue -
2020-07-16 10:58:51.078331-0700 Appname[44300:8867431] [AXRuntimeCommon] Unknown client: Appname
2020-07-16 10:58:51.084416-0700 Appname[44300:8867435] [AXRuntimeCommon] AX Lookup problem - errorCode:1100 error:Permission denied portName:'com.apple.iphone.axserver' PID:44186
Once the app loads it checks if the user is logged in on the home page with this function
func isLoggedIn() {
if Firebase.Auth.auth().currentUser == nil {
perform(#selector(handleLogout), with: nil, afterDelay: 0)
}
}
#objc func handleLogout() {
do {
try Auth.auth().signOut()
} catch let logoutError {
print("logout error", logoutError)
}
let startview = StartView()
startview.home = self
let nav = UINavigationController(rootViewController: startview)
nav.modalPresentationStyle = .fullScreen
present(nav, animated: false)
}
Then in the login page it logs the user in and runs the function from the home page but it just shows up as blank.
#objc func Login() {
Auth.auth().signIn(withEmail: EmailField.text!, password: PasswordField.text!) { [weak self] (user, error) in
guard let StrongSelf = self else {
return
}
guard let result = user, error == nil else {
print(error!._code)
self?.handleError(error!)
return
}
let user = result.user
print("logged in \(user)")
//NotificationCenter.default.post(name: NSNotification.Name(rawValue: "loadhome"), object: nil)
StrongSelf.navigationController?.dismiss(animated: true, completion: {
self?.home.loadfirstusers()
})
}
}
var home = HomePage()
It calls this function to update the user data and gets as far as printing sameunisamecourse but it doesn't call the print inside the dispatch.notify for some reason?
func SameUniSameCourse(completion: #escaping (_ success: Bool) -> Void) {
self.dispatchGroup.enter()
service.loadUniversityAndCourse { (uni, course) in
defer{ self.dispatchGroup.leave() }
let usersRef = Firestore.firestore().collection("users").order(by: "Created", descending: true).whereField("University", isEqualTo: uni).whereField("Course", isEqualTo: course)
self.dispatchGroup.enter()
usersRef.getDocuments { (snapshot, error) in
print("samecoursesameuni")
defer{ self.dispatchGroup.leave() }
if let error = error {
print(error.localizedDescription)
} else {
for document in snapshot!.documents {
let data = document.data()
//print(data)
if let dictionary = data as [String:AnyObject]? {
let Info = UserInfo(dictionary: dictionary)
if Info.uid == Auth.auth().currentUser?.uid {
//print(Info.username)
}
else {
self.sameUniSameCourse.append(Info)
//print(Info.username!)
}}}
}
}}
self.dispatchGroup.notify(queue: .main) {
print("dispatchNotifyCalled")
if self.sameUniSameCourse.isEmpty == true {
completion(false)
}
else {
self.masterArray.append(contentsOf: self.sameUniSameCourse)
self.spinner.stopAnimating()
completion(true)
}
}
}

Call web services by alamofire respectively

I'm creating an application that should call some web services in the begging of the app.
I've wrote a WebServiceManager that contains methods for calling web services and return result in completionHandler, here is one method of WebServiceManager:
extension WebServiceManager {
func checkVersion(completionHandler: #escaping (CheckVersionResponse?, Error?) -> ()) {
sessionManager?.adapter = BaseClientInterceptor()
let appVersion = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
let buildNumber = Bundle.main.infoDictionary!["CFBundleVersion"] as! String
sessionManager?.request(Config.VERSION_URL + "/\(appVersion)/\(buildNumber)" + Config.STATUS, method: .get).responseData { response in
print(response.response?.statusCode)
switch response.result {
case .success(let value):
print("----WS: checkVersion Success-----")
let value = JSON(value)
print(value)
let response = CheckVersionResponse(responseCode: response.response?.statusCode ?? -1)
if (response.getResponseCode() == 200) {
response.setUrl(value["url"].string ?? "")
response.setNow(value["now"].intValue)
response.setStatus(value["status"].stringValue)
response.setMessage(value["message"].stringValue)
}
completionHandler(response, nil)
case .failure(let error):
print(error)
print("----WS: checkVersion Failed-----")
completionHandler(nil, error)
}
}
}
}
and I use some methods of this manager in SplashScreenViewController:
class SplashViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
WebServiceManager.shared.checkVersion() { response, error in
//...
}
WebServiceManager.shared.getCurrentDate() { response, error in
//...
}
WebServiceManager.shared.getUpdatedKeyValues() { response, error in
//...
}
if !UserDefaults.standard.bool(forKey: "hasRegistered") {
self.performSegue(withIdentifier: "ToRegistration", sender: self)
} else {
self.performSegue(withIdentifier: "ToMain", sender: self)
}
}
}
I expect that this method runs line by line but it goes through to if at first.
How can I call these web services in order then make a decision based on UserDefaults values ?
sessionManager.request methods are asynchronous, so they will not execute synchronously (line by line) on viewDidAppear (by the way you are missing super.viewDidAppear())
If you want some code to execute after those 3 services you can do something like this, not sure if its the best solution tho:
class SplashViewController: UIViewController {
private var allDone = 0
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
WebServiceManager.shared.checkVersion() { response, error in
//...
checkUserDefault()
}
WebServiceManager.shared.getCurrentDate() { response, error in
//...
checkUserDefault()
}
WebServiceManager.shared.getUpdatedKeyValues() { response, error in
//...
checkUserDefault()
}
}
private func checkUserDefault() {
allDone += 1
/// When all 3 services are finish
if allDone == 3 {
if !UserDefaults.standard.bool(forKey: "hasRegistered") {
self.performSegue(withIdentifier: "ToRegistration", sender: self)
} else {
self.performSegue(withIdentifier: "ToMain", sender: self)
}
}
}
}
Hope it helps you!
EDIT: This is the simplest solution based on your existing code, if you want to do it 'the right way' you may want to check Promises: http://khanlou.com/2016/08/promises-in-swift/ https://www.raywenderlich.com/9208-getting-started-with-promisekit
You need to use PromiseKit and do something like this:
Replace your API methods so they use Promise as a response:
func checkVersion() -> Promise<CheckVersionResponse> {
sessionManager?.adapter = BaseClientInterceptor()
let appVersion = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
let buildNumber = Bundle.main.infoDictionary!["CFBundleVersion"] as! String
// Return a Promise for the caller of this function to use.
return Promise { fulfill, reject in
// Inside the Promise, make an HTTP request
sessionManager?.request(Config.VERSION_URL + "/\(appVersion)/\(buildNumber)" + Config.STATUS, method: .get).responseData { response in
print(response.response?.statusCode)
switch response.result {
case .success(let value):
print("----WS: checkVersion Success-----")
let value = JSON(value)
print(value)
let response = CheckVersionResponse(responseCode: response.response?.statusCode ?? -1)
if (response.getResponseCode() == 200) {
response.setUrl(value["url"].string ?? "")
response.setNow(value["now"].intValue)
response.setStatus(value["status"].stringValue)
response.setMessage(value["message"].stringValue)
}
fulfill(response)
case .failure(let error):
print(error)
print("----WS: checkVersion Failed-----")
reject(error)
}
}
}
}
Then on the UIViewController call the WebServiceManager methods like this:
WebServiceManager.shared.checkVersion().then { response in
// ....
WebServiceManager.shared.getCurrentDate()
}.then { date in
// ...
WebServiceManager.shared.getUpdatedKeyValues()
}.catch { error in
print(error)
}

sending information to another view controller

I found a barcode scanner project on git hub that I incorporated into my app link. I am using google books API to get information on the books I scanned.
func getBookInfo(isbn: String) {
guard let url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=isbn13:\(isbn)") else {
print("the url is not valid")
return
}
URLSession.shared.dataTask(with: url, completionHandler: {data, response, error -> Void in
guard error == nil else {
print(response)
print(error!.localizedDescription)
return
}
guard let data = data else {
print("no error but no data")
print(response)
return
}
guard let jsonResult = try? JSONSerialization.jsonObject(with: data, options: []) else {
print("the JSON is not valid")
return
}
if let arrayOfTitles = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.title") as? [String] {
self.BookName.text = "\(arrayOfTitles[0])"
print(self.BookName.text!)
}
if let arrayOfAuthors = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.authors") as? [[String]] {
self.Author.text = "\((arrayOfAuthors[0])[0])"
print(self.Author.text!)
}
if let arrayOfCategories = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.categories") as? [[String]] {
self.Category.text = "\((arrayOfCategories[0])[0])"
print(self.Category.text!)
}
if let arrayOfISBN13 = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.industryIdentifiers.identifier") as? [[String]] {
self.ISBN13.text = "\((arrayOfISBN13[0])[0])"
print(self.ISBN13.text!)
}
if let arrayOfISBN10 = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.industryIdentifiers.identifier") as? [[String]] {
self.ISBN10.text = "\((arrayOfISBN10[0])[1])"
print(self.ISBN10.text!)
}
if let arrayOfFormat = (jsonResult as AnyObject).value(forKeyPath: "items.volumeInfo.printType") as? [String] {
self.CoverType.text = "\(arrayOfFormat[0])"
print(self.CoverType.text!)
}
}).resume()
}
After I scan the books and have received the information, I would like to dismiss the view controller that has the barcode scanner and in the view controller that appears, I would like to display the information of the book I just scanned.
extension MultipleImageViewController: BarcodeScannerCodeDelegate {
func barcodeScanner(_ controller: BarcodeScannerController, didCaptureCode code: String, type: String) {
if code.isEmpty {
let delayTime = DispatchTime.now() + Double(Int64(6 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: delayTime) {
controller.resetWithError()
}
}
else{
getBookInfo(isbn: code)
let delayTime = DispatchTime.now() + Double(Int64(6 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: delayTime) {
controller.dismiss(animated: true, completion: nil)
}
}
}
}
However, when the barcode scanner view controller is dismissed, I have to exit the app and then come back to the app in order for my information to show in the view controller that I want it to. Without leaving the app and coming back the information that I received from the barcode scanner does not display in the desired view controller.
Your UI is not updating because the method viewDidLoad does not get called when that view controller is returned to since it already loaded. Instead, create a delegate for the parent that will get called when you dismiss the child view controller. Sort of like this:
class ParentViewController: UIViewController, BarcodeDelegate {
func presentChildViewController() {
let childViewController = ChildViewController()
childViewController.delegate = self
present(childViewController, animated: true, completion: nil)
}
func dismissedChildViewController(code: String) {
// update UI
}
}
class ChildViewController: UIViewController {
var delegate: BarcodeDelegate?
func dismiss() {
delegate.dismissedChildViewController(code: getCode())
dismiss(animated: true, completion: nil)
}
}
protocol BarcodeDelegate {
func dismissedChildViewController(code: String)
}
It is difficult to really understand what the problem is, so this may or may not be what you're looking for.

Not able to access on demand resource in other file using swift

I started to work on on demand resource. I'm able to download the resource but i'm not able to use those resources in other files. please some one help me in fixing this issue. PFA code.
public class RBPExtensions: NSObject {
class func prefechDataForTag(_ tagArray: [String], completion: #escaping (_ success: Bool) -> Void) {
if let tags = NSSet(array: tagArray) as? Set<String> {
let resourceRequest = NSBundleResourceRequest.init(tags: tags, bundle: Bundle.main)
resourceRequest.conditionallyBeginAccessingResources { (resourceFound) in
if resourceFound {
completion(true)
} else {
resourceRequest.beginAccessingResources(completionHandler: { (error) in
if error != nil {
completion(false)
} else {
let image = UIImage(named: "filter_button_filled")
completion(true)
}
})
}
}
} else {
completion(false)
}
}
}
calling this method as :
RBPExtensions.prefechDataForTag(["Image"], completion: { (success) in
})

Trying to implement Signup with Facebook function in my App

I have a custom button for users to register with Facebook. However when I test it is shows that I have authorized the app, but it stays on the safari page and does not return to my app. Here is what my code looks like so far.
#IBAction func facebookButtonWasHit(sender: AnyObject) {
let permissions = [ "public_profile", "email" ]
FBSDKLoginManager().logInWithReadPermissions(permissions, fromViewController: nil, handler: { (result, error) in
if error != nil {
self.presentViewController(UIAlertController(title: "Whoops!", message: error!.localizedDescription), animated: true, completion: nil)
}
else if result.isCancelled {
self.presentViewController(UIAlertController(title: "Whoops!", message: "We couldn't access facebook! Did you hit cancel?"), animated: true, completion: nil)
}
else {
if((FBSDKAccessToken.currentAccessToken()) == nil){
FBSDKGraphRequest(graphPath: "me", parameters:["fields":"email,name"]).startWithCompletionHandler({ (connection, result, error) in
if error != nil {
self.presentViewController(UIAlertController(title: "Whoops!", message: error!.localizedDescription), animated: true, completion: nil)
} else {
if let loginResult = result as? Dictionary<String,AnyObject> {
dispatch_async(dispatch_get_main_queue(), {
if let emailID = loginResult["email"] as? String{
self.emailTextField.text = emailID
}
self.nameTextField.text = loginResult["name"] as? String
let userID = loginResult["id"] as! String
let facebookProfileUrl = "http://graph.facebook.com/\(userID)/picture?type=large"
let url = NSURL(string:facebookProfileUrl)
self.picChanged = true
self.downloadImage(url!)
})
}
}
})
}
}
})
}

Resources