I am using Apple Pay with Stripe and it works fine when there is no shipping is available.
When there is a shipping address selected it always gives invalid address error. (It works with Stripe Sandbox Key)
I am using STKPaymentContext API and follow steps from the below link.
https://stripe.com/docs/mobile/ios/basic
in the configuration, I have written this.
let config = STPPaymentConfiguration.shared()
config.requiredShippingAddressFields = [.postalAddress, .phoneNumber,.name]
Not sure what is wrong here.
Here is how it looks.
HERE IS MY CODE
extension CheckoutTableViewController:STPPaymentContextDelegate{
func paymentContext(_ paymentContext: STPPaymentContext, didUpdateShippingAddress address: STPAddress, completion: #escaping STPShippingMethodsCompletionBlock) {
guard
let buyerPostalCode = address.postalCode,
let buyerCountry = address.country,
let productId = self.productDetailsData?.productId
else{
completion(.invalid, nil, nil, nil)
return
}
guard let phone = address.phone, phone.count > 0 else {
completion(.invalid,RatesError.phoneNumberRequired,[],nil)
return
}
var shipmentItem:[String:Any] = [:]
shipmentItem["order_amount"] = self.productCost
shipmentItem["actual_weight"] = 8
shipmentItem["height"] = 7
shipmentItem["width"] = 10
shipmentItem["length"] = 13
shipmentItem["currency"] = "USD"
shipmentItem["destination_postal_code"] = buyerPostalCode
shipmentItem["destination_country_code"] = buyerCountry
shipmentItem["product_id"] = productId
shipmentItem["category"] = "fashion"
enum RatesError:Error,LocalizedError{
case NoDeliveryOptionsFound
case phoneNumberRequired
public var errorDescription: String? {
switch self {
case .NoDeliveryOptionsFound:
return "No couriers are available at the address.\nPlease try with different address."
case .phoneNumberRequired:
return "Please enter phone number."
}
}
}
fetchShippingOptions(forItem: shipmentItem, completionSuccess: {[weak self] (response) in
guard let self = `self` else {
return
}
if
let responseValue = response as? [String:Any],
let rates = responseValue["rates"] as? [[String:Any]]{
self.shippingRates = []
for rate in rates{
if let fullName = rate["courier_display_name"] as? String,
let identifier = rate["courier_id"] as? String,
let amount = rate["shipment_charge_total"] as? Double,
let detail = rate["full_description"] as? String
{
let method = PKShippingMethod()
method.amount = NSDecimalNumber.init(value: amount.currency)
method.identifier = identifier
method.label = fullName
method.detail = detail.replacingOccurrences(of: fullName, with: "")
self.shippingRates.append(method)
}
}
completion(.valid, nil, self.shippingRates, self.shippingRates.first)
}else{
completion(.invalid,RatesError.NoDeliveryOptionsFound,[],nil)
}
}) { (error) in
completion(.invalid,error,[],nil)
}
}
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
if let paymentOption = paymentContext.selectedPaymentOption {
self.lblPaymentMethod.text = paymentOption.label
} else {
self.lblPaymentMethod.text = "Select Payment"
}
if let shippingMethod = paymentContext.selectedShippingMethod {
if let selectedRate = self.shippingRates.first(where: { (method) -> Bool in
guard let leftValue = method.identifier, let rightValue = shippingMethod.identifier else{
return false
}
return leftValue == rightValue
}){
self.lblAddress.text = selectedRate.label
self.shippingCharges = Double(truncating: selectedRate.amount).currency
self.lblShippingCharges.text = "$\(shippingCharges)"
self.getStripeFees(forAmount: self.productCost + self.shippingCharges)
}
} else {
self.lblAddress.text = "Select Address"
}
self.updateTotalCost()
}
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
let alertController = UIAlertController(
title: "Error",
message: error.localizedDescription,
preferredStyle: .alert
)
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: { action in
// Need to assign to _ because optional binding loses #discardableResult value
// https://bugs.swift.org/browse/SR-1681
_ = self.navigationController?.popViewController(animated: true)
})
let retry = UIAlertAction(title: "Retry", style: .default, handler: { action in
self.paymentContext?.retryLoading()
})
alertController.addAction(cancel)
alertController.addAction(retry)
self.present(alertController, animated: true, completion: nil)
}
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: #escaping STPPaymentStatusBlock) {
self.callPaymentIntentAPI(paymentContext, didCreatePaymentResult: paymentResult, completion: completion)
}
func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
OperationQueue.main.addOperation {
SVProgressHUD.dismiss()
}
let title: String
let message: String
switch status {
case .error:
title = "Error"
message = error?.localizedDescription ?? ""
UIAlertController.showAlert(withTitle: title, andMessage: message, andButtonTitles: ["Okay"]) {[weak self] (selectedIndex) in
OperationQueue.main.addOperation {
self?.navigationController?.popViewController(animated: true)
}
}
case .success:
title = "Success"
message = "Your purchase was successful!"
UIAlertController.showAlert(withTitle: title, andMessage: message, andButtonTitles: ["Okay"]) {[weak self] (selectedIndex) in
OperationQueue.main.addOperation {
self?.onPaymentCompletion?()
var isControllerFound:Bool = false
for controller in self?.navigationController?.viewControllers ?? []{
if (controller is ProductDetailsViewController) || (controller is ChatVC){
isControllerFound = true
self?.navigationController?.popToViewController(controller, animated: true)
break
}
}
if !isControllerFound{
self?.navigationController?.popViewController(animated: true)
}
}
}
case .userCancellation:
return()
#unknown default:
return()
}
}
}
Finally, I found an error.
Apple calls didUpdateShippingAddress method at the time of payment but it doesn't provide all information for security purposes. So in my case phone number validation was causing that error.
So I removed the below code from that method.
guard let phone = address.phone, phone.count > 0 else {
completion(.invalid,RatesError.phoneNumberRequired,[],nil)
return
}
Related
I'm working on my app (iOS) to get AdMob and AdSense earnings information. But I've been having trouble getting specifics from them. I've already created the credentials and client ID from my Google account, but I'm not sure where to put them.
I tried carefully following many methods from this link but was never successful.
My first step: During startup, check to see if you are logged in or out.
import FirebaseAuth
import GoogleSignIn
var currentPID = ""
func checkGoogleAccountStatus() {
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
if error != nil || user == nil {
print("Signed out")
self.loginButton()
} else {
print("Signed in")
let userAccess: String = user!.authentication.accessToken
self.googledSignedInSuccess(googleToken: userAccess, tokenID: user!.authentication.idToken!)
let dateformatter = DateFormatter()
dateformatter.dateFormat = "MMMM d, yyyy h:mm:ss a"
let expiredToken = user?.authentication.accessTokenExpirationDate
print("Token Expired: \(dateformatter.string(from: expiredToken!))")
}
}
}
Successful
My second step: When I tapped the button to log in, an alert controller appeared to see if the log in was successful. It will display the alert controller's profile picture, name, and email address.
#objc func loginTapped() {
let adMobScope = "https://www.googleapis.com/auth/admob.report"
let adSenseScope = "https://www.googleapis.com/auth/adsensehost"
let additionalScopes = [adMobScope,adSenseScope]
let signInConfig = GIDConfiguration.init(clientID: "<My URL Schemes>")
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self, hint: nil, additionalScopes: additionalScopes) { user, error in
guard error == nil else { return }
guard let user = user else { return }
if let profiledata = user.profile {
let grantedScopes = user.grantedScopes
if grantedScopes == nil || !grantedScopes!.contains(adMobScope) {
print("AdMob not Granted...")
} else {
print("AdMob Granted!")
}
if grantedScopes == nil || !grantedScopes!.contains(adSenseScope) {
print("AdSense not Granted...")
} else {
print("AdSense Granted!")
}
//let userId: String = user.userID ?? ""
let givenName: String = profiledata.givenName ?? ""
let familyName: String = profiledata.familyName ?? ""
let email: String = profiledata.email
let userToken: String = user.authentication.idToken!
let userAccess: String = user.authentication.accessToken
let credential = GoogleAuthProvider.credential(withIDToken: userToken, accessToken: userAccess)
Auth.auth().signIn(with: credential) { result, error in
if let error = error {
print(error.localizedDescription)
let alert = UIAlertController(title: "Error", message: "Something went wrong, please try again.", preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .cancel, handler: {_ in return })
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
if let imgurl = user.profile?.imageURL(withDimension: 300) {
let absoluteurl: String = imgurl.absoluteString
let alert = UIAlertController(title: "\(givenName) \(familyName)", message: "\n\n\n\n\n\n\n\(email)\nLogin Successful", preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .cancel, handler: {_ in
// MARK: Do something to update
self.checkGoogleAccountStatus()
})
let imgViewTitle = UIImageView()
imgViewTitle.translatesAutoresizingMaskIntoConstraints = false
imgViewTitle.layer.borderColor = UIColor(named: "Font Color")?.cgColor
imgViewTitle.layer.borderWidth = 3
imgViewTitle.layer.cornerRadius = 50
imgViewTitle.clipsToBounds = true
alert.view.addSubview(imgViewTitle)
imgViewTitle.centerYAnchor.constraint(equalTo: alert.view.centerYAnchor, constant: -28).isActive = true
imgViewTitle.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor).isActive = true
imgViewTitle.widthAnchor.constraint(equalToConstant: 100).isActive = true
imgViewTitle.heightAnchor.constraint(equalToConstant: 100).isActive = true
DispatchQueue.global().async {
if let data = try? Data(contentsOf: URL(string: absoluteurl)! ) { if let image = UIImage(data: data) { DispatchQueue.main.async { imgViewTitle.image = image } } }
}
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
} else {
let alert = UIAlertController(title: "\(givenName) \(familyName)", message: "\(email)\nLogin Successful", preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .cancel, handler: {_ in
// MARK: Do something to update
self.checkGoogleAccountStatus()
})
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
}
}
}
}
When I logged in, it asked for permission to access AdMob and AdSense, followed by a pop-up alert that said I had successfully logged in.
My third step: Getting the PID from Google AdMob / AdSense
import CurlDSL
import Gzip
func googledSignedInSuccess(googleToken: String, tokenID: String) {
guard let url = URL(string: "https://admob.googleapis.com/v1/accounts/") else { return }
do {
try CURL(#"curl -H "Authorization: Bearer \#(googleToken)" "\#(url)""#).run { data, response, error in
if let error = error { print("Error took place \(error)"); return }
if let response = response as? HTTPURLResponse {
if response.statusCode != 200 {
print("Error: \(response)")
} else {
if let data = data {
do {
if let rawJSON = try? JSONDecoder().decode(GetAdMobInfo.self, from: data) {
currentPID = rawJSON.account[0].publisherID
print("Successful: \(currentPID)")
self.adMob_gettingReport(pid: currentPID, token: googleToken)
}
}
}
}
}
}
} catch { print("Failed.") }
}
struct GetAdMobInfo: Codable {
let account: [Account]
}
struct Account: Codable {
let name, publisherID, reportingTimeZone, currencyCode: String
enum CodingKeys: String, CodingKey {
case name
case publisherID = "publisherId"
case reportingTimeZone, currencyCode
}
}
It was success, I was able to get my PID and writed to currentPID as string.
My final step, which failed:
func adMob_gettingReport(pid: String, token: String) {
guard let url = URL(string: "https://admob.googleapis.com/v1/accounts/\(pid)/mediationReport:generate") else { return }
let reportData = "--data #- << EOF {\"report_spec\": {\"date_range\": {\"start_date\": {\"year\": 2020, \"month\": 4, \"day\": 1}, \"end_date\": {\"year\": 2020, \"month\": 4, \"day\": 1} },\"dimensions\": [\"AD_SOURCE\", \"AD_UNIT\", \"PLATFORM\"], \"metrics\": [\"ESTIMATED_EARNINGS\"]}} EOF"
do {
try CURL(#"curl -X POST "\#(url)" -H "Authorization: Bearer \#(token)" -H "Content-Type: application/json" \#(reportData)"#).run { data, response, error in
if let error = error { print("Error took place \(error)"); return }
if let response = response as? HTTPURLResponse {
if response.statusCode != 200 {
print("Error: \(response)")
} else {
if let data = data {
print("Getting AdMob Successful")
let decompressedData: Data
if data.isGzipped { decompressedData = try! data.gunzipped() }
else { decompressedData = data }
var getLineFromString: [String] = []
getLineFromString += String(data: decompressedData, encoding: .utf8)!.components(separatedBy: "\n")
for checkLine in getLineFromString {
print("Line: \(checkLine)")
}
}
}
}
}
} catch { print("Failed.") }
}
Attempting to obtain earnings information from AdMob and AdSense, but it kept saying failing in print. This is where I've been for nearly two months. What did I overlook?
I'm using the firebase for chat application and I've already stored the images in firebase storage now I want to get the url to fetch the user profile but it's not working even I've tried but nothing is working
Here is the function on line 3rd it generates an error (Value of type 'DatabaseReference' has no member 'downloadURL')
func downloadURL(for path: String, completion: #escaping (Result<URL, Error>) -> Void) {
let refrence = database.child(path)
refrence.downloadURL(completion: { url, error in //here downnloadUrl not working perfectly
guard let url = url, error == nil else {
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
completion(.success(url))
})
}
NetworkingService Class
struct NetworkingService{
static let shared = NetworkingService()
private init(){
}
private let database = Database.database().reference() //creating refrence
public func test(){
database.child("name").setValue(["faheem":true])
}
static func emailForImage(emailAddress: String) -> String{
var safeEmail = emailAddress.replacingOccurrences(of: ".", with: "-")
safeEmail = safeEmail.replacingOccurrences(of: "#", with: "-")
return safeEmail
}
func downloadURL(for path: String, completion: #escaping (Result<URL, Error>) -> Void) {
let refrence = database.child(path)
refrence.downloadURL(completion: { url, error in
guard let url = url, error == nil else {
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
completion(.success(url))
})
}
func userExists(email: String,completion: #escaping((Bool) -> Void)){ //return boolen
var safeEmail = email.replacingOccurrences(of: ".", with: "-")
safeEmail = safeEmail.replacingOccurrences(of: "#", with: "-")
database.child(safeEmail).observeSingleEvent(of: .value) { (snapshot) in
guard let foundEmail = snapshot.value as? String else {
completion(false)
return
}
}
completion(true)
}
func insertUser(user: CUser, completion: #escaping(Result<CUser,Error>) -> Void){
database.child(user.identifier).setValue(user.getDict) { (err, dbRef) in
if err != nil{
completion(.failure(err!))
}else{
completion(.success(user))
}
}
}
}
StoreManager Class
struct StoreManager{
static let shared = StoreManager()
private let storageRefrence = Storage.storage().reference()
func uploadProfilePic(data: Data, fileName: String, completion: #escaping(Result<String,Error>) -> Void){
storageRefrence.child("images/\(fileName)").putData(data,metadata: nil) { (metaData, error) in
guard error == nil else {
completion(.failure(AppError.failedToUpload))
print("faheem ye he error \(error?.localizedDescription)")
return
}
self.storageRefrence.child("images/\(fileName)").downloadURL { (url, error) in
guard let url = url else {
completion(.failure(AppError.failedtoDownloadUrl))
return
}
let urlString = url.absoluteString
print("download url is\(urlString)")
completion(.success(urlString))
}
}
}
}
SignupUserClass
class SignUpUserViewController: UIViewController {
#IBOutlet weak var userProfile: UIImageView!
#IBOutlet weak var lname: UITextField!
#IBOutlet weak var fname: UITextField!
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
var imagePicker = UIImagePickerController() // for imagepicker
private let spinner = JGProgressHUD(style: .dark)
var user:CUser!
override func viewDidLoad() {
super.viewDidLoad()
password.isSecureTextEntry = true
userProfile.layer.masksToBounds = true
userProfile.layer.borderWidth = 2
userProfile.layer.borderColor = UIColor.lightGray.cgColor
userProfile.layer.cornerRadius = userProfile.frame.size.height / 2
// for rounder image
userProfile.isUserInteractionEnabled = true /
let gesture = UITapGestureRecognizer(target: self, action: #selector(userProfileChange)) //photopcker
userProfile.addGestureRecognizer(gesture)
}
#objc private func userProfileChange(){
print("profile pic changed")
getPhoto()
}
private func insertUser(_ user:CUser){
NetworkingService.shared.insertUser(user: user) { (result) in
switch result{
case .success(let user):
print("User inserted: ", user.getDict)
CUser.shared = user
print("sahed Data is fahem\(CUser.shared)")
case .failure(let error):
print("Error: ",error.localizedDescription)
self.showAlert(message: error.localizedDescription)
}
}
}
private func createUser(_ user:CUser){
FirebaseAuth.Auth.auth().createUser(withEmail: user.email, password: user.password) { [weak self] (respomse, error) in
guard let data = respomse else {
print("Error: ",error?.localizedDescription ?? "")
self?.showAlert(message: error?.localizedDescription ?? "")
return
}
self?.insertUser(user)
}
}
private func uploadImage(_ imageData:Data) {
StoreManager.shared.uploadProfilePic(data: imageData, fileName: user.imageName) { (result) in
switch result{
case .success(let url):
self.user.imageURL = url
self.createUser(self.user)
case .failure(let error):
print("Error: ",error.localizedDescription)
self.showAlert(message: error.localizedDescription)
}
}
}
// register User
#IBAction func register(_ sender: UIButton) {
if checkTextFields([email,password,fname,lname]) {
showAlert(message: "Some fields are missing")
}else{
spinner.show(in: self.view)
NetworkingService.shared.userExists(email: email.text!) { (response) in//checking user through email
self.spinner.dismiss()
guard response != nil else { //measn user exists
self.showAlert(message: "user account already exits we verify through email")
return
}
self.user = CUser(firstName: self.fname.text!, lastName: self.lname.text!, email: self.email.text!, password: self.password.text!, imageURL: "")
/*image url is nil becuase we not upload the image here wehn calling the upload image method
then after setting create user method here we only setting the data in user model and url nil*/
if let userImage = self.userProfile.image!.jpegData(compressionQuality: 0.2) {
self.uploadImage(userImage)
}else{
print("Error: Image cannot be compressed")
}
}
}
}
func checkTextFields(_ textfields:[UITextField]) -> Bool{
for textfield in textfields {
return textfield.text?.isEmpty == true ? true: false
}
return true
}
func gotOLogin(){
guard let loginController = self.storyboard?.instantiateViewController(withIdentifier: String(describing: LoginUserViewController.self)) as? LoginUserViewController else {return}
self.navigationController?.pushViewController(loginController, animated: true)
}
func showAlert(title:String = "Error", message:String){
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let ok = UIAlertAction(title: "ok", style:.default, handler: nil)
alertController.addAction(ok)
self.present(alertController, animated: true, completion: nil)
}
}
extension SignUpUserViewController : UIImagePickerControllerDelegate, UINavigationControllerDelegate{
func getPhoto(){
let actionSheet = UIAlertController(title: "Choose Image", message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
actionSheet.addAction(UIAlertAction(title: "Take Photo", style: .default, handler: {
[weak self] _ in
self?.presentCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Choose Photo", style: .default, handler: { [weak self] _ in
self?.profilePhotoPicker()
}))
present(actionSheet, animated: true, completion: nil)
}
func presentCamera(){ //not allowed in simualtor to capture photo
imagePicker.sourceType = .camera
imagePicker.delegate = self
imagePicker.allowsEditing = true
present(imagePicker, animated: true, completion: nil)
}
func profilePhotoPicker(){
imagePicker.sourceType = .photoLibrary
imagePicker.delegate = self
imagePicker.allowsEditing = true
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { //called when select photo
print(info)
userProfile.image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
imagePicker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
ProfileViewcontroler here is getProfilePic fun i already get all the necessary thing but issue is that in networking class download url is not working
class ProfileViewController: UIViewController {
var logoutUser = ["lgout"]
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var currentUserPic: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: String(describing: ProfileCell.self), bundle: .main), forCellReuseIdentifier: String(describing: ProfileCell.self))
}
func getProfilePic(){
guard let userEmail = UserDefaults.standard.value(forKey: "useremail") as? String else {
return}
let userEmailForPic = NetworkingService.emailForImage(emailAddress: userEmail)
let fileName = userEmailForPic + "_profile_image_jpg"
let patheStorage = "images/"+fileName//here i want to fetch the pic but in netwroking class downnload url not working
}
}
Firebase Realtime Database and Firebase Storage are separated services. You should be using downloadURL on a storage reference:
func downloadURL(for path: String, completion: #escaping (Result<URL, Error>) -> Void) {
let reference = Storage.storage().reference().child(path)
reference.downloadURL(completion: { url, error in
guard let url = url, error == nil else {
completion(.failure(StorageErrors.failedToGetDownloadUrl))
return
}
completion(.success(url))
})
}
I have this json wherein hospitalNumber has value and there is instances that it returns null value. The hospitalNumber has significance since its a part of the parameter needed for the endpoint in API. Please see sample json:
{
"responseMessage": "Request successful",
"data": [
{
"hospitalNumber": null,
"patientName": "Manual Entry",
"totalAmount": 10339.8000,
"manualEntry": true
},
{
"hospitalNumber": "1111111",
"patientName": "test patient",
"totalAmount": 932.5000,
"manualEntry": false
}
]
}
And below is my APIService for the endpoint that will pull the json above.
typealias getPatientDetailsPerPayoutTaskCompletion = (_ patientDetailsPerPayout: [PatientPayoutDetails]?, _ error: NetworkError?) -> Void
//Patient procedure details per patient
//parameterName is .searchByHospitalNumber = "hospitalNumber"
static func getPatientDetailsPerPayout(periodId: Int, doctorNumber: String, parameterName: PatientParameter, hospitalNumber: String, manualEntry: Bool, completion: #escaping getPatientDetailsPerPayoutTaskCompletion) {
guard let patientDetailsPerPayoutURL = URL(string: "\(Endpoint.Patient.patientProcedureDetails)?periodId=\(periodId)&doctorNumber=\(doctorNumber)\(parameterName.rawValue)\(hospitalNumber)&manualEntry=\(manualEntry)") else {
completion(nil, .invalidURL)
return
}
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getAllTasks { (tasks) in
tasks.forEach({ $0.cancel() })
}
Alamofire.request(patientDetailsPerPayoutURL, method: .get, encoding: JSONEncoding.default).responseJSON { (response) in
print(patientDetailsPerPayoutURL)
guard HelperMethods.reachability(responseResult: response.result) else {
completion(nil, .noNetwork)
return
}
guard let statusCode = response.response?.statusCode else {
completion(nil, .noStatusCode)
return
}
switch(statusCode) {
case 200:
guard let jsonData = response.data else {
completion(nil, .invalidJSON)
return
}
let decoder = JSONDecoder()
do {
let patientDetailsPayout = try decoder.decode(RootPatientPayoutDetails.self, from: jsonData)
if (patientDetailsPayout.data?.isEmpty)! {
completion(nil, .noRecordFound)
} else {
completion(patientDetailsPayout.data, nil)
}
} catch {
completion(nil, .invalidJSON)
}
case 400: completion(nil, .badRequest)
case 404: completion(nil, .noRecordFound)
default:
print("**UNCAPTURED STATUS CODE FROM (getPatientDetailsPayout)\nSTATUS CODE: \(statusCode)")
completion(nil, .uncapturedStatusCode)
}
}
}
getPatientPayoutDetails Function
func getPerPatientPayoutDetails(from: String, manualEntry: Bool) {
//SVProgressHUD.setDefaultMaskType(.black)
//SVProgressHUD.setForegroundColor(.white)
SVProgressHUD.setBackgroundColor(.lightGray)
SVProgressHUD.show(withStatus: "Retrieving Patient Procedures")
APIService.PatientList.getPatientDetailsPerPayout(periodId: doctorPayoutWeek[3].periodId!, doctorNumber: doctorNumber, parameterName: .selectedByHospitalNumber, hospitalNumber: from, manualEntry: manualEntry) { (patientPayout, error) in
guard let patientPerPayoutDetails = patientPayout, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: "No Record Found", message: "You don't have current payment remittance", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
case .noNetwork:
let alertController = UIAlertController(title: "No Network", message: "\(networkError.rawValue)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: "Error", message: "There is something went wrong. Please try again", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
SVProgressHUD.dismiss()
return
}
self.selectedPatientPayment = patientPerPayoutDetails
print(self.selectedPatientPayment)
SVProgressHUD.dismiss()
return
}
}
tableView
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 0: break
case 1: break
case 2:
filteredPatient = indexPath.row
let selectedpatient = patientList[filteredPatient].hospitalNumber
let selectedEntry = patientList[filteredPatient].manualEntry
self.isBrowseAll = false
getPerPatientPayoutDetails(from: selectedpatient!, manualEntry: selectedEntry)
default: break
}
}
The endpoint which requires null string in hospitalNumber when it is nil
https://sample.com/openapi/getpatientpayoutdetails?periodId=579&doctorNumber=2866&hospitalNumber=null&manualEntry=true
As you can see hospital number has an important role for the endpoint. My problem is, once the tableView has reloaded it shows the data properly but when I didSelect the cell with null hospitalNumber, my app crashes and show Found nil error since hospitalNumber has null value. Hope you understand what I am trying to explain, please help me. Thank you
Your Codable model is correct, all you need is guard-let/if-let to prevent crash:
if let selectedpatient = patientList[filteredPatient].hospitalNumber, let selectedEntry = patientList[filteredPatient].manualEntry {
self.isBrowseAll = false
getPerPatientPayoutDetails(from: selectedpatient, manualEntry: selectedEntry)
}
Updated:
If you want to create endPoint in case of nil also then use coalescing operator :
getPerPatientPayoutDetails(from: selectedpatient ?? "null", manualEntry: selectedEntry)
In didSelect do like
let selectedpatient:String! //or Int whatever type it is right not this line initializes selected patient with nil
//below line will check for nil and ALSO NULL if NULL or nil it will not reassigne selectedpatient which means selectedpatient will remain nil
if let hsptlNbr = patientList[filteredPatient].hospitalNumber as? yourDataType{
selectedpatient = hsptlNbr
}
after this you can pass this as nil or value if exist in below method
getPerPatientPayoutDetails(from: selectedpatient, manualEntry: selectedEntry)
Change func getPerPatientPayoutDetails(from: String, manualEntry: Bool)
to
func getPerPatientPayoutDetails(from: String?, manualEntry: Bool)
I'm trying to use a func I extended UIViewController with. I added self to the different func's arguments but the func not gives me "Implicit use of 'self' in closure; use 'self.' to make capture semantics explicit" error. Tried even to apply a weak self reference. The func seems to be never running anyway
te array is declared
var managedComicsArray = [NSManagedObject]()
in viewDidLoad :
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
managedContext = appDelegate.persistentContainer.viewContext
the button
#IBAction func addComicButtonTapped(_ sender: UIBarButtonItem) {
print("add button tapped")
weak var weakSelf = self // ADD THIS LINE AS WELL
let alert = UIAlertController(title: "Some Title", message: "Enter a text", preferredStyle: .alert)
alert.addTextField { (textField) in
textField.placeholder = "Some default text"
textField.keyboardType = .numberPad
}
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] (_) in
// let textField = alert.textFields![0] // Force unwrapping because we know it exists.
let textField = alert!.textFields![0]
print("Text from textField: \(textField.text)")
guard let grabbedNumber = Int16(textField.description) else {return}
//the func below gives the error
weakSelf?.addRecordIfnotPresent(newIssueNumber: grabbedNumber, theManagedContext: self.managedContext, theEntityName: self.kComicEntityName, managedArray: &self.managedComicsArray)
self.myTableView.reloadData()
}))
alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
// 4. Present the alert.
self.present(alert, animated: true, completion: nil)
//end of button
}
the extended method:
func addRecordIfnotPresent(newIssueNumber: Int16, theManagedContext: NSManagedObjectContext, theEntityName: String, managedArray: inout [NSManagedObject]) {
print("adding func started")
let comicFetch: NSFetchRequest<Comic> = Comic.fetchRequest()
var isPresent = true
do {
managedArray = try theManagedContext.fetch(comicFetch)
if !managedArray.isEmpty {
//*****************************************************************
for comic in managedArray {
var comicToCheck = Comic()
comicToCheck = comic as! Comic
if comicToCheck.wholeSeriesNumber == newIssueNumber {
print("number \(newIssueNumber) is alredy present")
return
} else {
isPresent = false
print("number not present, keep on!")
}
}
guard isPresent == false else {return}
saveSingleComicWithNumber(withIssueNumber: Int(newIssueNumber), theEntityName: theEntityName, theManagedContext: theManagedContext)
print("created Comic!")
try theManagedContext.save()
print("saved context!")
//*****************************************************************
} else {
saveSingleComicWithNumber(withIssueNumber: Int(newIssueNumber), theEntityName: theEntityName, theManagedContext: theManagedContext)
}
} catch let error as NSError {
print("Fetch error: \(error) description: \(error.userInfo)")
}
}
func saveSingleComicWithNumber(withIssueNumber: Int, theEntityName: String, theManagedContext: NSManagedObjectContext) {
let entity = NSEntityDescription.entity(forEntityName: theEntityName, in: theManagedContext)!
let comicToAdd = Comic(entity: entity, insertInto: theManagedContext)
comicToAdd.wholeSeriesNumber = Int16(withIssueNumber)
comicToAdd.issueInCollection = false
comicToAdd.titleOfItalianSerie = "title"
if comicToAdd.wholeSeriesNumber == 2 {
comicToAdd.issueInCollection = false
}
do {
try theManagedContext.save()
} catch let error as NSError {
print("could not save. \(error), \(error.userInfo)")
}
}
How can i upload a row in Parse.com?
This is my query code:
func queryFromParse(){
self.arrayOfDetails.removeAll()
let query = PFQuery(className: "currentUploads")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let newObjects = objects as? [PFObject] {
for oneobject in newObjects {
let text = oneobject["imageText"] as! String
let username = oneobject["username"] as! String
let deviceID = oneobject["identifierForVendor"] as! String
let reportedCount = oneobject["reportedCount"] as! String
let time = oneobject.createdAt!
if let userImage = oneobject["imageFile"] as? PFFile {
let userImage = oneobject["imageFile"] as! PFFile
let imageURL = userImage.url // <- Bruker nĂ¥ userImage.URL, henter ikke bildefilen med en gang
let OneBigObject = Details(username: username, text: text, CreatedAt: time, image: imageURL!, deviceID: deviceID, reportedCount: reportedCount)
//let OneBigObject = Details(username: username, text: text, CreatedAt: time, image: imageURL!)
self.arrayOfDetails.append(OneBigObject)
dispatch_async(dispatch_get_main_queue()) { self.collectionView.reloadData() }
}
}
}
}
}
}
Image here
I want to update the "reportedCount" when the image is reported. I have a code that i have been using, but that created new rows in another class for each report, and I want only to update the "reportedCount":
#IBAction func reportContentAction(sender: AnyObject) {
let buttonPosition = sender.convertPoint(CGPointZero, toView: self.collectionView)
let indexPath = self.collectionView.indexPathForItemAtPoint(buttonPosition)
////
println(indexPath?.item)
////
let post = self.arrayOfDetails[indexPath!.item]
var alertMessage = NSString(format:"*User: %#\r *Text: %#\r *Created at %#", post.username, post.text, post.CreatedAt)
var reportAlert = UIAlertController(title: "Report Content", message:alertMessage as String, preferredStyle: UIAlertControllerStyle.Alert)
reportAlert.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Report Logic here")
var currentUploads = PFObject(className: "banned")
currentUploads["username"] = post.username
currentUploads["imageText"] = post.text
currentUploads["imageFile"] = post.image
currentUploads["identifierForVendor"] = post.deviceID
currentUploads["flaggedBy"] = PFUser.currentUser()?.username
currentUploads["flaggedByUUID"] = UIDevice.currentDevice().identifierForVendor.UUIDString
currentUploads.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if error == nil{
//**Success saving, now save image.**//
currentUploads.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if error == nil{
// Take user home
print("Data uploaded")
// Show UIAlertView
let alert = UIAlertView()
alert.title = "Message"
alert.message = "You report has been sent. Thank you for your support."
alert.addButtonWithTitle("Close")
alert.show()
}
else{
print(error)
}
})
}
else{
print(error)
}
})
}))
reportAlert.addAction(UIAlertAction(title: "Cancel", style: .Default, handler: { (action: UIAlertAction!) in
println("Handle Cancel Logic here")
}))
presentViewController(reportAlert, animated: true, completion: nil)
}
You should be keeping a reference to oneobject, or more broadly, all of the objects returned from the query. Now when something changes you can get the appropriate instance from the objects array and then update and save it to modify the existing 'row'.