I got my email working fine. The only thing i need to do is add an additional string to my message body. For example, I want to add Name: and textfield to my message body. Something like this.
Name: John Smith
Phone: 566-654-6577
Email: Smith#smith.com
Right now, the message body only show the following.
John Smith
566-654-6577
Smith#smith.com
import UIKit
import MessageUI
class EmailTableViewController: UITableViewController, MFMailComposeViewControllerDelegate, UITextFieldDelegate {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var phone: UITextField!
#IBOutlet weak var email: UITextField!
func appendTextFromTextField(string: String, textField: UITextField) -> String {
return string + textField.text + "\n \n"
}
#IBAction func SendEmailButton(sender: AnyObject) {
var fields: [UITextField] = [name, phone, email]
var messageBody = ""
for f in fields {
messageBody = appendTextFromTextField(messageBody, textField: f)
}
var emailTitle = "Interface Information"
var toRecipents = [""]
var mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setSubject(emailTitle)
mc.setMessageBody(messageBody, isHTML: false)
mc.setToRecipients(toRecipents)
self.presentViewController(mc, animated: true, completion: nil)
}
func mailComposeController(controller:MFMailComposeViewController, didFinishWithResult result:MFMailComposeResult, error:NSError) {
switch result.value {
case MFMailComposeResultCancelled.value:
println("Mail cancelled")
case MFMailComposeResultSaved.value:
println("Mail saved")
case MFMailComposeResultSent.value:
println("Mail sent")
case MFMailComposeResultFailed.value:
println("Mail sent failure: %#", [error.localizedDescription])
default:
break
}
self.dismissViewControllerAnimated(true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Remove the for loop and try replacing your method with this one:
#IBAction func sendEmailButton(sender: UIButton) {
var messageBody = "Name:\(name.text)\nPhone:\(phone.text)\nEmail:\(email.text) "
var emailTitle = "Interface Information"
var toRecipents = [""]
var mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setSubject(emailTitle)
mc.setMessageBody(messageBody, isHTML: false)
mc.setToRecipients(toRecipents)
self.presentViewController(mc, animated: true, completion: nil)
}
You can use the reduce and zip2 functions to do something like this.
let fields: [UITextField] = [name , phone, email]
let fieldNames : [String] = ["Name", "Phone" ,"Email"]
let messageBody = reduce(Zip2(fields,fieldNames),"") { body,zipped in
let (field,fieldName) = zipped
return body + fieldName + " : " + field.text + " \n\n "
}
Replace the
var fields: [UITextField] = [name, phone, email]
var messageBody = ""
for f in fields {
messageBody = appendTextFromTextField(messageBody, textField: f)
}
with:
var messageBody = ""
let addOn:String = "name: (name.text)\nphone: (phone.text)\nemail:(email.text)\n"
messageBody = messageBody + addOn
Also, I am not sure why you have a tableViewController. If you dont put a table in your storyboard you will get a runtime error
Related
Hi every one I am trying to build E-Commerce IOS app , and I am using Stripe payment gateway with VSCode
but when I run this line of code : paymentContext.requestPayment()
it gives me this error : Error: Must provide source or customer
here is my Checkout view controller
class CheckoutViewController: UIViewController, CartItemDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var selectPaymentMethodBtn: UIButton!
#IBOutlet weak var selectShippingMethod: UIButton!
#IBOutlet weak var serviceFeeLabel: UILabel!
#IBOutlet weak var delivaryFeeLabel: UILabel!
#IBOutlet weak var subtotalLabel: UILabel!
#IBOutlet weak var totalLabel: UILabel!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
var paymentContext : STPPaymentContext!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupTableView()
setupPaymentInfo()
setupStripeConfig()
}
func setupTableView(){
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: K.cellIdentifiers.CartItemCell, bundle: nil), forCellReuseIdentifier: K.cellIdentifiers.CartItemCell)
}
func setupPaymentInfo(){
serviceFeeLabel.text = StripeCart.proccessingFees.penniesToFormattedCurrency()
delivaryFeeLabel.text = StripeCart.shippingFees.penniesToFormattedCurrency()
subtotalLabel.text = StripeCart.subtotal.penniesToFormattedCurrency()
totalLabel.text = StripeCart.total.penniesToFormattedCurrency()
}
func setupStripeConfig(){
let config = STPPaymentConfiguration.shared
config.requiredBillingAddressFields = .full
//config.requiredShippingAddressFields = [.name ,.phoneNumber]
let customerContext = STPCustomerContext(keyProvider: StripeApi)
paymentContext = STPPaymentContext(customerContext: customerContext, configuration: config, theme: .defaultTheme)
paymentContext.paymentAmount = StripeCart.total
paymentContext.delegate = self
paymentContext.hostViewController = self
}
#IBAction func selectPaymentMethodClicked(_ sender: UIButton) {
paymentContext.pushPaymentOptionsViewController()
}
#IBAction func selectShippingMethodClicked(_ sender: UIButton) {
paymentContext.pushShippingViewController()
}
func removeItem(product: Product) {
StripeCart.removeItemFromCart(item: product)
tableView.reloadData()
setupPaymentInfo()
paymentContext.paymentAmount = StripeCart.total
}
#IBAction func placeOrderClicked(_ sender: RoundedButton) {
paymentContext.requestPayment()
activityIndicator.startAnimating()
}
}
and here is my STPPaymentContextDelegate code
extension CheckoutViewController : STPPaymentContextDelegate {
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
if let paymentMethod = paymentContext.selectedPaymentOption {
selectPaymentMethodBtn.setTitle(paymentMethod.label, for: .normal)
} else {
selectPaymentMethodBtn.setTitle("Select Method", for: .normal)
}
}
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
let alertController = UIAlertController(title: "ERROR", message: error.localizedDescription, preferredStyle: .alert)
let cancel = UIAlertAction(title: "Cancel", style: .cancel) { action in
self.navigationController?.popViewController(animated: true)
}
let retry = UIAlertAction(title: "Retry", style: .default) { action in
self.paymentContext.retryLoading()
}
}
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: #escaping STPPaymentStatusBlock) {
let idempotency = UUID().uuidString.replacingOccurrences(of: "-", with: "")
let data : [String : Any] = [
"total" : StripeCart.total ,
"customerID" : UserService.user.stripID ,
"idempotency" : idempotency
]
Functions.functions().httpsCallable("makeCharge").call(data) { result, error in
if let error = error {
print(error.localizedDescription)
completion(STPPaymentStatus.error , error)
return
}
StripeCart.clearCart()
self.tableView.reloadData()
self.setupPaymentInfo()
completion(STPPaymentStatus.success, nil)
}
}
func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
let title : String
let msg : String
switch status {
case .success:
activityIndicator.stopAnimating()
title = "SUCCESS"
msg = "THX FOR YOUR SHOPPING"
case .error:
activityIndicator.stopAnimating()
title = "ERROR"
msg = error?.localizedDescription ?? ""
case .userCancellation:
return
}
let alert = UIAlertController(title: title, message: msg, preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default) { action in
self.navigationController?.popViewController(animated: true)
}
alert.addAction(action)
self.present(alert, animated: true , completion: nil)
}
}
and here is my index.js code
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp()
const stripe = require("stripe")(functions.config().stripe.secret_test_key)
exports.createStripeCustomer = functions.firestore.document('users/{id}').onCreate(async (snap, context) => {
const data = snap.data();
const email = data.email;
const customer = await stripe.customers.create({ email: email })
return admin.firestore().collection('users').doc(data.id).update({ stripID: customer.id })
});
exports.makeCharge = functions.https.onCall(async (data , context) => {
const customerID = data.customer_id;
const total = data.total;
const idempotency = data.idempotency;
const uid = context.auth.uid;
if (uid === null) {
console.log('Illegal Access')
throw new functions.https.HttpsError('permission-denied' , 'Illegal access attempt')
}
return stripe.charges.create({
amount: total,
currency: 'JOD',
customer: customerID
}, {
idempotency_key: idempotency
}).then( _ => {
return
}).catch(err => {
console.log(err);
throw new functions.https.HttpsError('internal' , err + 'Ln tutemm alsheraa')
});
})
exports.createEphemeralKey = functions.https.onCall(async (data, context) => {
const customerID = data.customer_id;
const stripeVersion = data.stripe_version;
const uid = context.auth.uid;
if (uid === null) {
console.log('Illegal Access')
throw new functions.https.HttpsError('permission-denied' , 'Illegal access attempt')
}
return stripe.ephemeralKeys.create(
{customer: customerID} ,
{stripe_version: stripeVersion}
).then((key) => {
return key
}).catch((err) => {
console.log(err)
throw new functions.https.HttpsError('internal' , 'unable to create ephemeral key')
})
})
so how could I solve this problem
Note : I am Living in Jordan , I don't know why I told you that but I think it makes different 🤷🏼
I found that the problem is inside stripe.charges.create({ ... }) code , thats why when I printed the customerID with error it gives me (.undefined) , and thats why when I check my stripe Logs for charge event I found this
My Charge Logs
but where is the problem exactly 🤷🏼 ???
I tried to add my real visa but it showed me error says : Your card was declined
I tried to add the customerID manually hardcoded into my index.js but it shows me this error : "Cannot charge a customer that has no active card"
The error message Must provide source or customer means that you're trying to create a charge but either the source(card) is missing from the request or a customer ID that has a saved payment method in order for the charge to work.
The client-side code you've provided looks fine but would recommend checking your Stripe logs (https://dashboard.stripe.com/test/logs) to validate that if you're adding in a new source then the request to attach the source to customer object isn't failing.
I'm pretty certain that the error is coming from stripe.charges.create({ ... }). You'd want to check if the customerID is indeed present and if it is, does that customer have a source attached.
I have one class that i call RealTimeAPI and then the actual LoginViewController class and what i want to do is i want to add my register method in my RealtimeApi class and call register in my LoginView. But then i need to errorhandle in my LoginView so i need to throw the error from the completionhandler in the registerfunction RealtimeAPIs Createuser. is this possible or no? this is my code so far.
RealtimeAPIClass
import Foundation
import Firebase
enum RegisterError: Error
{
case IncompleteForm
case NonMatchingForm
case FirebaseError
}
class RealTimeApi
{
private let Reference: DatabaseReference! = Database.database().reference()
private var nilOrNot = [Bool]()
public var errorDescription: String?
func Register(FullName: String?, Username: String?, Email: String?, EmailVerification: String?, Password: String?, PasswordVerification: String? )
{
Auth.auth().createUser(withEmail: Email!, password: Password, completion: er, Error){
}
}
func Login(Username:String, Password: String)
{
}
func CheckLoggedinUser() -> Bool
{
let currentuser = Auth.auth().currentUser
if(currentuser == nil)
{
return false
}else{
return true
}
}
}
Loginview
import UIKit
import Firebase
class LogInV: UIViewController {
#IBOutlet weak var UsernameTxt: UITextField!
#IBOutlet weak var PasswordTxt: UITextField!
#IBOutlet var TextfieldRegistrationCollection: [UITextField]!
#IBOutlet weak var ImageView: UIView!
#IBOutlet weak var RegisterView: UIView!
#IBOutlet weak var RV_VerticalAlignmentConstraint: NSLayoutConstraint!
#IBOutlet weak var RegisterBtnO: UIButton!
var Data = RealTimeApi()
var TextFieldStyle = TextfieldStyling()
override func viewDidLoad() {
super.viewDidLoad()
TextFieldStyle.StylizeTextField(StylizedTextField: UsernameTxt)
TextFieldStyle.StylizeTextField(StylizedTextField: PasswordTxt)
for i in 0...TextfieldRegistrationCollection.count - 1 {
TextFieldStyle.StylizeTextField(StylizedTextField: TextfieldRegistrationCollection[i])
}
TextfieldValidation()
RV_VerticalAlignmentConstraint.constant += view.bounds.height
}
override func viewDidAppear(_ animated: Bool) {
RegisterBtnO.isEnabled = false
}
#IBAction func LoginButtons(_ sender: UIButton) {
switch sender.tag{
case 0:
break
case 1:
Slide()
break
default:
print("button not pressed")
break
}
}
func Slide()
{
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.RV_VerticalAlignmentConstraint.constant -= self.view.bounds.height
self.view.layoutIfNeeded()
}, completion: nil)
}
#IBAction func RegisterBtn(_ sender: UIButton) {
}
//Validate textfields so that user register does not work for empty input
func TextfieldValidation()
{
for i in 0...TextfieldRegistrationCollection.count - 1
{
TextfieldRegistrationCollection[i].addTarget(self, action: #selector(LogInV.textFieldDidChange), for: UIControlEvents.editingChanged)
}
}
//selector function for controlling empty textfield
#objc func textFieldDidChange(){
var NoneIsEmpty = Int()
for i in 0...TextfieldRegistrationCollection.count - 1{
if let text = TextfieldRegistrationCollection[i].text, text.isEmpty == false {
NoneIsEmpty += 1
}
}
if(NoneIsEmpty == TextfieldRegistrationCollection.count)
{
RegisterBtnO.isEnabled = true
}else{
RegisterBtnO.isEnabled = false
}
NoneIsEmpty = 0
}
func showAlert(error: String)
{
let Erroralert = UIAlertController(title: "Error", message: error, preferredStyle: .alert)
Erroralert.addAction(UIAlertAction(title: "Dissmiss",style: .cancel ,handler: {action in
print("tapped actionbutton")
}))
present(Erroralert, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
}
}
func UploadCustomerList(customer: Customer)
{
}
}
So basically i want to do something like this
Auth.auth().createUser(withEmail: Email!, password: Password, completion: User, Error){
throw Error
}
I have created a helper class with the following method:
func setupAlert(with title: String, with message: String?, viewController: UIViewController) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let cancel = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alert.addAction(cancel)
viewController.present(alert, animated: true, completion: nil)
}
you must pass the viewcontroller in the parameter where you want to display the alert. Then you can also pass the error, which can be found in Auth.auth()
I am new here and would like to ask a question that has been working for me for days. I'm just learning Swift 4 and I've come quite a long way. I really do not know what to do any more, and my books on swift do not help me either.
I have created a small testapp, in which should simply be charged.
There are 5 view controllers. The first one has 4 buttons to get to one of the other 4 and to enter a number there in a text box. This number is then output in the first viewcontroller in a label. The numbers are displayed and even the last entered number is displayed again after a restart of the app.
But now I want to charge off the numbers in the first viewcontroller. How can I fix the code?
My Viewports:
my viewports
code from main viewport:
import UIKit
class ViewController: UIViewController, sendValue1, sendValue2, sendValue3, sendValue4 {
#IBOutlet weak var value1: UILabel!
#IBOutlet weak var value2: UILabel!
#IBOutlet weak var value3: UILabel!
#IBOutlet weak var value4: UILabel!
#IBOutlet weak var calculatedValue1: UILabel! // here i want to see the calculated value like from the label 1-4...value1 + value2 + value3 + value4 = ???
#IBOutlet weak var calculatedValue2: UILabel! // here the same like in claculatedValue1 value but with "-" or "*" or something else...
func value1Data(data: String) {
value1.text = data
UserDefaults.standard.set(value1.text, forKey: "value1")
}
func value2Data(data: String) {
value2.text = data
UserDefaults.standard.set(value2.text, forKey: "value2")
}
func value3Data(data: String) {
value3.text = data
UserDefaults.standard.set(value3.text, forKey: "value3")
}
func value4Data(data: String) {
value4.text = data
UserDefaults.standard.set(value4.text, forKey: "value4")
}
override func viewDidAppear(_ animated: Bool) {
if let lastValue1Data = UserDefaults.standard.object(forKey: "value1") as? String {
value1.text = lastValue1Data
}
if let lastValue2Data = UserDefaults.standard.object(forKey: "value2") as? String {
value2.text = lastValue2Data
}
if let lastValue3Data = UserDefaults.standard.object(forKey: "value3") as? String {
value3.text = lastValue3Data
}
if let LastValue4Data = UserDefaults.standard.object(forKey: "value4") as? String {
value4.text = LastValue4Data
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "VC1" {
let SendingVC1: Value1ViewController = segue.destination as! Value1ViewController
SendingVC1.delegate = self
}
if segue.identifier == "VC2" {
let SendingVC2: Value2ViewController = segue.destination as! Value2ViewController
SendingVC2.delegate = self
}
if segue.identifier == "VC3" {
let SendingVC3: Value3ViewController = segue.destination as! Value3ViewController
SendingVC3.delegate = self
}
if segue.identifier == "VC4" {
let SendingVC4: Value4ViewController = segue.destination as! Value4ViewController
SendingVC4.delegate = self
}
}
#IBAction func unwindToView1(_ segue: UIStoryboardSegue) {
}
and the code from one of the other four:
import UIKit
protocol sendValue1 {
func value1Data(data: String)
}
class Value1ViewController: UIViewController {
var delegate: sendValue1? = nil
#IBOutlet weak var textValue1: UITextField!
#IBAction func done(_ sender: Any) {
if delegate != nil {
if textValue1.text != nil {
let data = textValue1.text
delegate?.value1Data(data: data!)
dismiss(animated: true, completion: nil)
}
}
}
why is the result always nil here?
let a = Float(value3.text!) ?? 0
let b = Float(value4.text!) ?? 0
let SUM = a + b
calculatedValue1.text = "\(SUM)" + "m"
No matter what I do, the numbers are not processed ...
I have an Xcode project which has some error. I had only a problem with a variable. I convert the txt string variable to int and this called txtint. When I want to make an calculation I can't because it has null value what is impossible because I give a value in function "pass" and then when I want to subtract I can't because txtint has null value.
//
// ViewController.swift
// Biophere
//
// Created by Coder on 2017. 10. 22..
// Copyright © 2017. Pliz Help. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var textview: UITextField!
#IBOutlet weak var conv: UILabel!
var text: String? = nil
var txt: String? = nil
var convert: String? = nil
var textint: Int? = nil
var txtint: Int? = nil
#IBAction func pass(_ sender: Any) {
var txt: String {
get {
return textview.text ?? ""
}
set {
textview.text = newValue
}
}
//conv.text = txt
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
label.text = text
txt = textview.text
}
#IBAction func saveButton(_ sender: Any) {
let textint = Int(text!)
let txtint = Int(txt!)
convert = String(textint! - txtint!)
conv.text = txt
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ERROR: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
var text: String = ""
var txt: String = ""
var convert: String = ""
var textint: Int = 0
var txtint: Int = 0
#IBAction func saveButton(_ sender: Any) {
if let textint = Int(text), let txtint = Int(txt) {
convert = String(textint - txtint)
conv.text = txt
}else{
print("handle it")
}
}
Suggestion: Try to give variable name descriptive it is a basic thing for development.
//Patient class
import Foundation
struct Patients {
var family: NSArray
var given: NSArray
var id: String
var birthdate:String
var gender: String
}
struct Address {
var city: String
var country: String
var line: NSArray
}
class Patient {
var flag = 0
var address = Address(city: "", country: "", line: [""])
var patient_info = Patients(family: [""], given: [""], id: "", birthdate: "", gender: "")
var response : AnyObject?
init(response: AnyObject) {
self.response = response
if let entry = response.objectForKey("entry") {
//MARK: Address
if let resource = entry[0].objectForKey("resource") {
if let add = resource.objectForKey("address") {
address.city = add[0].objectForKey("city")! as! String
address.country = add[0].objectForKey("country")! as! String
address.line = add[0].objectForKey("line")! as! NSArray
//MARK: patient
patient_info.birthdate = resource.objectForKey("birthDate")! as! String
patient_info.gender = resource.objectForKey("gender")! as! String
if let name = resource.objectForKey("name") {
patient_info.family = name[0].objectForKey("family")! as! NSArray
patient_info.given = name[0].objectForKey("given")! as! NSArray
}
}
}
//MARK: id
if let link = entry[0].objectForKey("link") {
if let url = link[0].objectForKey("url") {
let id = url.componentsSeparatedByString("/")
patient_info.id = id[id.count-1]
}
}
}
print(patient_info)
}
}
//ViewController class
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
var viewModel = ViewModel()
#IBOutlet weak var family_name: UITextField!
#IBOutlet weak var given_name: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
family_name.delegate = self
given_name.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
switch textField {
case family_name:
family_name.resignFirstResponder()
given_name.becomeFirstResponder()
case given_name:
given_name .resignFirstResponder()
default:
print("")
}
return true
}
#IBAction func search(sender: UIButton) {
let family_name1 = family_name.text!
let given_name1 = given_name.text!
viewModel .searchForPatient(family_name1, given_name: given_name1)
//When the name property from my patient class changed I can call the //below method. How to implement the observer?
performSegueWithIdentifier("showSegue", sender:sender)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender:AnyObject?){
if segue.identifier == "showPatientSegue" {
if let displayViewController = segue.destinationViewController as? DisplayViewController {
displayViewController.viewModelDisplay.patient = viewModel.patient
}
}
}
}
// ViewModel where I make the request.
import Foundation
import Alamofire
import SystemConfiguration
class ViewModel {
var patient = Patient!()
func searchForPatient(family_name: String, given_name : String) {
let header = ["Accept" : "application/json"]
Alamofire.request(.GET, "https://open-ic.epic.com/FHIR/api/FHIR/DSTU2/Patient?family=\(family_name)&given=\(given_name)", headers: header).responseJSON { response in
self.patient = Patient(response: response.result.value!)
}
}
func checkInternetConnection() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}
var flags = SCNetworkReachabilityFlags()
if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
return false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection)
}
}
The problem is that the view loads fester than the request and I need to observe when a property has been changed in my Patient class, so the view can be loaded. If the view loads faster than the request I can't display the Patient information which I need.
You have lots options:
Store a delegate (weak!) object to the ViewController so that when your patient finishes, you can load the view. In the meantime, display something sensible in the view instead.
Send an NSNotification out, which the ViewController is a listener for.
KVO (Explanation of it here, just search for 'key-value observing'), which would require your Patient object to extend NSObject so that you can leverage objc's KVO.
Hope that helps!
You can add an observer on your variable this way :
var yourVariable:String!{
didSet{
refreshView()
}
}