I have a situation where I need to allow the user to select a contacts mailing address if the contact has multiple addresses. If there are multiple addresses I present the user with a tableView with the names of the addresses. When the user taps one of the address names I want to pull the address data and populate a field. What I need help with is getting a contacts mailing address using the mailing address label?The function buildContactsAddress_Array below builds an array containing the address label(name) and the address ID. The array is used to populate a tableView where the user can select the address by its name. I have included pretty much all of the related code to try and make things as clear as I can. Thanks in advance.This is the part I want to change or replace so it uses the addressID. Right now it just uses the first/home address.if let firstPostalAddress = (theName.postalAddresses.first),
let labelValuePair = firstPostalAddress.value(forKey: "labelValuePair") as? AnyObject,
let finalPostalAddress = labelValuePair.value(forKey: "value") as? CNPostalAddress
{
mailAddress = CNPostalAddressFormatter.string(from: finalPostalAddress, style: .mailingAddress)
}struct contactAddresses
{
var theLabel: String
var theID: String
}
private var addressesArray = [contactAddresses]()
private var addressID: String = ""
private var theContactID: String = ""This function pulls the contacts info using the contacts ID.func getContactFromID_Ouote(contactID: String)
{
let store = CNContactStore()
var theName = CNContact()
let theKeys = [CNContactNamePrefixKey,
CNContactGivenNameKey,
CNContactFamilyNameKey,
CNContactOrganizationNameKey,
CNContactPostalAddressesKey,
CNContactFormatter.descriptorForRequiredKeys(for: .fullName)] as! [CNKeyDescriptor]
do {
theName = try store.unifiedContact(withIdentifier: contactID, keysToFetch: theKeys)
contactName = CNContactFormatter.string(from: theName, style: .fullName)!
contactPrefix = theName.namePrefix
contactFirst = theName.givenName
contactLast = theName.familyName
companyName = theName.organizationName == "" ? "" : theName.organizationName
} catch {
print("Fetching contact data failed: \(error)")
}
if let firstPostalAddress = (theName.postalAddresses.first),
let labelValuePair = firstPostalAddress.value(forKey: "labelValuePair") as? NSObject,
let finalPostalAddress = labelValuePair.value(forKey: "value") as? CNPostalAddress
{
mailAddress = CNPostalAddressFormatter.string(from: finalPostalAddress, style: .mailingAddress)
}
}This function puts the contacts addresses into an array. The array is then used to populate a tableView.func buildContactsAddress_Array(contactID: String)
{
let store = CNContactStore()
var theName = CNContact()
let theKeys = [CNContactPostalAddressesKey] as [CNKeyDescriptor]
do {
theName = try store.unifiedContact(withIdentifier: contactID, keysToFetch: theKeys)
let postalAddress = theName.postalAddresses
postalAddress.forEach { (mailAddress) in
// Strip forst 4 and last 4 from _$!!$_
let aaa = mailAddress.label
let bbb = aaa!.dropLast(4)
let ccc = bbb.dropFirst(4)
addressesArray.append(contactAddresses(theLabel: String(ccc), theID: mailAddress.identifier))
}
addressesArray.sort { $0.theLabel < $1.theLabel }
} catch {
print("Fetching contact addresses failed: \(error)")
}
}This is the tableView extension. When a cell is tapped, addressID is populated with the ID of the appropriate mailing address.extension QuotePreview_VC: UITableViewDelegate, UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return addressesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let theCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
theCell.textLabel?.text = addressesArray[indexPath.row].theLabel
return theCell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
addressID = addressesArray[indexPath.row].theID
populateThePrintFld()
closeThePicker()
}
}
Post
Replies
Boosts
Views
Activity
I am trying to allow the user to edit a contact that is defined by the contacts identifier.The contacts identifier is in - private var theContactID: String = ""This code opens the CNContactViewController but I don't know how to tell the controller to open the specified contact for editing. I appreciate any help.private var theContactID: String = ""
let theAlert = UIAlertController(title: NSLocalizedString("ContactNoAddress_String", comment: ""), message: nil, preferredStyle: .alert)
let cancelBtn = UIAlertAction(title: NSLocalizedString("OK_String", comment: ""), style: .cancel) { (cancelAction) in
self.populateThePrintFld()
}
let editBtn = UIAlertAction(title: NSLocalizedString("EditContact_String", comment: ""), style: .default) { (editAction) in
self.requestAccess { (accessGranted) in
let editContact = CNMutableContact()
let vc = CNContactViewController(for: editContact)
vc.allowsEditing = true
vc.delegate = self // delegate for CNContactViewControllerDelegate
DispatchQueue.main.async {
self.present(UINavigationController(rootViewController: vc), animated: true)
}
}
}
theAlert.addAction(cancelBtn)
theAlert.addAction(editBtn)
present(theAlert, animated: true)
Hi,Just checking to see if I'm missing something. In iOS 13.3.1 preferredStatusBarStyle fired and worked fine. In iOS 13.4 and 13.4.1 is doesn't fire at all. I thought this was supposed to be fixed in iOS 13.4.1I have themes set up in my app and they still work fine in iOS 13.3.1override var preferredStatusBarStyle: UIStatusBarStyle
{
// print("preferredStatusBarStyle fired")
return Theme.current.barStyle
}
In a @IBDesignable this allows me to set the font of a textView.How can I do the something similar for a textField?Thnaks in advance.This is how I use the code below on a textView.balanceDueTextView_Outlet.placeholderFont = UIFont(name: Theme.current.readingFont, size: fontSize)?.italic()These are in a IBDesignableoverride open var font: UIFont! {
didSet {
if placeholderFont == nil {
placeholderLabel.font = font
}
}
}
open var placeholderFont: UIFont? {
didSet {
let font = (placeholderFont != nil) ? placeholderFont : self.font
placeholderLabel.font = font
}
}This is the extension I'm using.extension UIFont
{
func withTraits(_ traits:UIFontDescriptor.SymbolicTraits...) -> UIFont
{
let descriptor = self.fontDescriptor
.withSymbolicTraits(UIFontDescriptor.SymbolicTraits(traits).union(self.fontDescriptor.symbolicTraits))
return UIFont(descriptor: descriptor!, size: 0)
}
func withoutTraits(_ traits:UIFontDescriptor.SymbolicTraits...) -> UIFont
{
let descriptor = self.fontDescriptor
.withSymbolicTraits( self.fontDescriptor.symbolicTraits.subtracting(UIFontDescriptor.SymbolicTraits(traits)))
return UIFont(descriptor: descriptor!, size: 0)
}
func bold() -> UIFont
{
return withTraits(.traitBold)
}
func italic() -> UIFont
{
return withTraits(.traitItalic)
}
func noItalic() -> UIFont
{
return withoutTraits(.traitItalic)
}
func noBold() -> UIFont
{
return withoutTraits(.traitBold)
}
}
invalid mode 'kCFRunLoopCommonModes' provided to CFRunLoopRunSpecific - break on _CFRunLoopError_RunCalledWithInvalidMode to debugI get this warning when I tap either of the switches shown below. I've tried capturing the switch state in a var and using that to trigger the do/catch statement but no joy. I've even tried pulling the do/catch into separate functions and I still get the warning. Has anybody else run into this and how did you fix it?@IBAction func greetingFormat_Tapped(_ sender: UISwitch)
{
let theQuery = theTable_Settings.filter(settingID == 1)
if sender.isOn
{
do {
if try Database.shared.databaseConnection!.run(theQuery.update(greeting_Format <- "true")) > 0
{
greetingFormatLabel_Outlet.text = NSLocalizedString("HelloMrSmith_String", comment: "")
} else {
print("greeting format true not found")
}
} catch {
print("greeting format true update failed! Error: \(error)")
}
} else {
do {
if try Database.shared.databaseConnection!.run(theQuery.update(greeting_Format <- "false")) > 0
{
greetingFormatLabel_Outlet.text = NSLocalizedString("HiJoe_String", comment: "")
} else {
print("greeting format false not found")
}
} catch {
print("greeting format false update failed! Error: \(error)")
}
}
}@IBAction func nonrefundableSwitch_Tapped(_ sender: UISwitch)
{
let theQuery = theTable_Settings.filter(settingID == 1)
var itsOn: String = ""
if sender.isOn
{
itsOn = "true"
} else {
itsOn = "false"
}
if itsOn == "true"
{
do {
if try Database.shared.databaseConnection!.run(theQuery.update(nonRefundable_Bool <- "true")) > 0
{
depositDueLabel_Outlet.text = NSLocalizedString("nonRefunddepositisdue_String", comment: "")
} else {
print("nonRefundable true not found")
}
} catch {
print("nonRefundable true update failed! Error: \(error)")
}
} else {
do {
if try Database.shared.databaseConnection!.run(theQuery.update(nonRefundable_Bool <- "false")) > 0
{
depositDueLabel_Outlet.text = NSLocalizedString("depositisdue_String", comment: "")
} else {
print("nonRefundable false not found")
}
} catch {
print("nonRefundable false update failed! Error: \(error)")
}
}
}
Hi, I've just started reworking my iPhone app so that it will look good on an iPad. I wasn't paying much attention to the available iPad simulator devices until now. I see that the iPad Mini and iPad 10.5 are missing. Are they not there on purpose ie. are they not capable of running 13.4.1? I've searched for this information but I haven't found anything definitive. I'm running Catalina 10.15.4 and Xcode 11.4.1 Thanks
The code below allows the user to do a 2 finger swipe down on an imageView and thus presenting a popover/actionSheet. That process works fine. Normally it is possible to tap outside the popover/actionSheet to close it.The problem is that once the popover/actionSheet is presented, it doesn't allow tapping the background to close the popover/actionSheet. You actually need to tap inside the popover/actionSheet to close it.There are other places in the app that present a popover/actionSheet but these are presented using a simple button tap.Here's the really weird scenario. If I do the 2 finger swipe on the imageView and open the popover/actionSheet, the inability to tap the backGround is broken on all the other popover/actionSheet in the app too. If I bypass the 2 finger swipe on the imageView all of the other popover/actionSheet work as normal.I've stripped out all the code other than what's needed to present the popover/actionSheet. And I created a new project with on VC and one imageView so as to eliminate any possible conflict with cocoa pod, etc. What is wrong with this code?class ViewController: UIViewController
{
@IBOutlet weak var imageView_Outlet: UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
imageView_Outlet.isUserInteractionEnabled = true
let swipeGuesture = UISwipeGestureRecognizer(target: self, action: #selector(imageViewSwiped(recognizer:)))
swipeGuesture.numberOfTouchesRequired = 2
swipeGuesture.direction = .down
imageView_Outlet.addGestureRecognizer(swipeGuesture)
}
@objc func imageViewSwiped(recognizer: UISwipeGestureRecognizer)
{
let theAlert = UIAlertController(title: "Welcome Image", message: "Only one image can be saved as your welcome screen. The current image will automatically be replaced." , preferredStyle: .actionSheet)
let chooseImage = UIAlertAction(title: "Choose a New Image", style: .default, handler: { (okAction) in
})
let deleteBtn = UIAlertAction(title: "Delete the Current Image", style: .destructive, handler: { (deleteAction) in
})
let cancelBtn = UIAlertAction(title: "Cancel", style: .cancel) { (cancelAction) in
}
theAlert.addAction(cancelBtn)
theAlert.addAction(chooseImage)
theAlert.addAction(deleteBtn)
let popOver = theAlert.popoverPresentationController
popOver?.sourceView = self.imageView_Outlet
popOver?.sourceRect = self.imageView_Outlet.bounds
popOver?.permittedArrowDirections = .any
present(theAlert, animated: true)
}
}
Hi,There are two view involved here. For clarity call them VC1 and popup VC.The popup VC is presented as a popover segue from VC1 and the segue is defined in the IB.What I want to do is when the popup VC is dismissed I need to trigger the tableView array to be re-built and the tableView to be reloaded on VC1. I know how to do tableview work. However, viewDidAppear, viewDidLoad, viewWillAppear, etc. obviously don't run in this circumstance. What I haven't been able to figure out is how to determine when VC1 becomes active again in order to run the necessary tableView code.Thanks in advance.The only additional code for the segue is below.override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "tally_pop"
{
let popupVC = segue.destination as! Tallly_Popover_VC
popupVC.preferredContentSize = CGSize(width: 300, height: 462)
}
}
Hi,This involves four fields on a view. Three of the fields are controlled to present a currency value with the sum of two of the fields (paidFld and shippingFld) been put into a third (totalFld) field. This part works fine. I'm just including it for clarity purposes.The fourth field (conditionFld) adds a % to the end of the input and the length of the input is restricted to 3 or less characters. This is handeled in the extension.My question is, how to restrict the input of the conditionFld to be a number between 1 and 100? The keyboard type of this field is set to number pad.Thanks for the help.Tomclass ViewController: UIViewController
{
@IBOutlet weak var conditionFld: UITextField!
@IBOutlet weak var paidFld: UITextField!
@IBOutlet weak var shippingFld: UITextField!
@IBOutlet weak var totalField: UITextField!
let Condition_Field_Length = 3
override func viewDidLoad()
{
super.viewDidLoad()
hideKeyboard()
}
func convert(theField: UITextField)
{
let paidStr: String = theField.text!
let tempPaid = (paidStr as NSString).doubleValue
if theField.text! == ""
{
return
} else if let theDouble = convertCurrencyToDouble(input: theField.text!) {
theField.text = convertDoubleToCurrency(amount: theDouble)
} else {
theField.text = convertDoubleToCurrency(amount: tempPaid)
}
}
func convertCurrencyToDouble(input: String) -> Double?
{
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.locale = Locale.current
return numberFormatter.number(from: input)?.doubleValue
}
func convertDoubleToCurrency(amount: Double) -> String
{
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .currency
numberFormatter.locale = Locale.current
return numberFormatter.string(from: NSNumber(value: amount))!
}
@IBAction func conditionEditingBegin(_ sender: UITextField)
{
let theString: String = sender.text!
if sender.keyboardType == .numberPad
{
if theString != ""
{
let newString = String.init(theString.dropLast() )
sender.text = newString
}
}
}
@IBAction func conditionEditingEnded(_ sender: UITextField)
{
let theString: String? = sender.text
if sender.keyboardType == .numberPad
{
if theString != ""
{
if theString?.suffix(1) != "%"
{
sender.text = "\(theString!)%"
}
} else {
sender.text = ""
}
}
}
@IBAction func paidEditingEnded(_ sender: UITextField)
{
convert(theField: sender)
let paidStr: String = paidFld.text!
let shippingStr: String = shippingFld.text!
let tPaid = convertCurrencyToDouble(input: paidStr)
let tShip = convertCurrencyToDouble(input: shippingStr)
let theTotalText = (Double(tPaid ?? 0.0) + Double(tShip ?? 0.0))
if theTotalText != 0.0 {
totalField.text = convertDoubleToCurrency(amount: theTotalText)
} else {
totalField.text = ""
}
}
}
extension UIViewController
{
func hideKeyboard()
{
let tap: UITapGestureRecognizer = UITapGestureRecognizer(
target: self,
action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
@objc func dismissKeyboard()
{
view.endEditing(true)
}
}
extension ViewController: UITextFieldDelegate
{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
// This is for the condition field
if textField.tag == 1
{
// How do I restrict the number values to be between 1 and 100
return (textField.text?.count ?? 0) + string.count - range.length <= Condition_Field_Length
} else {
// This if for the paid and shipping fields
guard let text = textField.text, let decimalSeparator = NSLocale.current.decimalSeparator else {
return true
}
var splitText = text.components(separatedBy: decimalSeparator)
let totalDecimalSeparators = splitText.count - 1
let isEditingEnd = (text.count - 3) < range.lowerBound
splitText.removeFirst()
// Check if it will exceed 2 dp
if splitText.last?.count ?? 0 > 1 && string.count != 0 && isEditingEnd
{
return false
}
// If there is already a decimalSeparator we don't want to allow further decimalSeparator
if totalDecimalSeparators > 0 && string == decimalSeparator
{
return false
}
// Keyboard is set to deciaml pad
return true
}
}
}
I'm trying to build a menu using the code below. I've run into some difficulty declaring the destination view in the struct.
I get the error: Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements
The designation of course will be the a view in the app.
Any help would be greatly appreciated.
import SwiftUI
struct MenuList: Identifiable {
		var id: Int
		var image: String
		var name: String
		var destination: View // This line is where the problem is
}
extension MenuList {
		static func getMenu() -> [MenuList] {
				return[
						MenuList(id: 1, image: "house", name: "Welcome", destination: Welcome_View()),
						MenuList(id: 2, image: "text.justify", name: "MW List", destination: TrickList_View()),
						MenuList(id: 3, image: "list.bullet", name: "My Collection", destination: MyCollection_View()),
						MenuList(id: 4, image: "text.badge.plus", name: "Telly Sheet", destination: Tally_View()),
						MenuList(id: 5, image: "rectangle.and.paperclip", name: "Want List", destination: WantList_View()),
						MenuList(id: 6, image: "gear", name: "Settings", destination: Settings_View()),
						MenuList(id: 7, image: "arrowshape.turn.up.right", name: "Share", destination: Share_View()),
						MenuList(id: 8, image: "questionmark.circle.fill", name: "Help", destination: Help_View()),
						MenuList(id: 9, image: "photo.on.rectangle", name: "Presentation", destination: Presentation_View())]
		}
}
How is this handled in SwiftUI?
		func chooseImage()
		{
				let imagePicker = UIImagePickerController()
				imagePicker.delegate = self
				if UIImagePickerController.isSourceTypeAvailable(.photoLibrary)
				{
						PHPhotoLibrary.requestAuthorization({ (status) in
								switch status
								{
								case .authorized:
										DispatchQueue.main.async {
												imagePicker.sourceType = .photoLibrary
												imagePicker.allowsEditing = self.cropStatus
												self.present(imagePicker, animated: true)
										}
								case .notDetermined:
										DispatchQueue.main.async {
												imagePicker.sourceType = .photoLibrary
												imagePicker.allowsEditing = self.cropStatus
												self.present(imagePicker, animated: true)
										}
								case .restricted:
										self.view.sendConfirmationAlert(theTitle: "Photo Library Restricted", theMessage: "Photo Library access was previously denied. Please update your Settings to allow access.", buttonTitle: "OK")
								case .denied:
										let settingsAlert = UIAlertController(title: "Photo Library Access Denied", message: "Photo Library access was previously denied. Please update your Settings to allow access.", preferredStyle: .alert)
										let settingsAction = UIAlertAction(title: "Go to Settings", style: .default) { ( action ) in
												let settingsUrl = URL(string: UIApplication.openSettingsURLString)
												UIApplication.shared.open(settingsUrl!, options: [:])
										}
										let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
										settingsAlert.addAction(settingsAction)
										settingsAlert.addAction(cancelAction)
										self.present(settingsAlert, animated: true)
								default:
										return
								}
						})
				}
		}
This extension send the alert.
import UIKit
import SystemConfiguration
extension UIView {
		//MARK: - Send a confirmation alert
		func sendConfirmationAlert(theTitle: String, theMessage: String?, buttonTitle: String)
		{
				let theAlert = UIAlertController.init(title: theTitle, message: theMessage, preferredStyle: .alert)
				let theAction = UIAlertAction(title: buttonTitle, style: .default)
				theAlert.addAction(theAction)
				//present it on controller
				let window = UIApplication.shared.windows.first { $0.isKeyWindow }
				if let navController = window?.rootViewController as? UINavigationController
				{
						navController.present(theAlert, animated: true)
				}
		}
}
I understand the mechanics of creating the bundle, that's pretty straight forward.
I currently have six apps released on the app store and they are each set up with in-app purchase. I have the apps set to free on the Pricing and Availability screen.
I want to create an app bundle to include all six apps.
From what I can tell I cannot use the in-app purchase versions in a bundle. Is this correct or do I need to change how I have the IAP set up?
Or do I need a separate paid app version for each so to include in the bundle.
If I do need to create six more apps on the app store can I use the existing app ID and profile or do I need both a new profile and app ID?
For my current apps I have 2 profiles each, a Production and a Development and obviously one identifier.
Also, what do I need to do with the Xcode project itself? If I make a copy of the Xcode project and there make the required modifications, that would mean I have to maintain 2 xcode projects for each app. That doesn't make sense.
Any help would be greatly appreciated,
Tom
I have a number of apps which use a custom tab-bar for navigation. The tab-bar uses buttons with an ICON on top and text below. I have this working in Xcode 12.5 with an extension similar to those below. I wanted to see if I could convert to Xcode 13 and take advantage of the new button configurations. I was having some problems getting this to work properly so I did the following testing.
I created a new empty project in Xcode 12.5.1. Closed and re-opened the app in Xcode 13 and added two plain buttons. I didn’t change any of the attributes in the IB so they just have a blue button text. Then I attached one of the extensions below to each button. When the app opens in iSO 15 the buttons display the correct style. However, when either of the buttons is tapped the display reverts back to the text attributes of the button in the IB. So it’s blue, the text reads button and the text size changes back to the default. The ICON and the positioning remain unchanged.
If I create a new Xcode 13 project and do the exact same thing then everything working fine. Is anybody else seeing this? Is there something I need to change something in the extensions to make it work in pre 13 Xcode?
@available(iOS 15.0, *)
extension UIButton
{
func settings_MenuBtn()
{
self.configuration = .plain()
let imageConfig = UIImage.SymbolConfiguration(scale: .large)
self.configuration?.title = "Settings"
self.configuration?.attributedTitle?.foregroundColor = .white
self.configuration?.imagePlacement = .top
self.configuration?.titleAlignment = .center
self.configuration?.imagePadding = 6
self.configuration?.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
self.configuration?.image = UIImage(systemName: "gearshape", withConfiguration: imageConfig)
self.configuration?.attributedTitle?.font = .systemFont(ofSize: gMenuTextSize, weight: .regular)
}
}
@available(iOS 15.0, *)
extension UIButton.Configuration
{
static func settings_MenuBtn2() -> UIButton.Configuration
{
let imageConfig = UIImage.SymbolConfiguration(scale: .large)
var config: UIButton.Configuration = .plain()
config.title = "Settings"
config.attributedTitle?.foregroundColor = .white
config.attributedTitle?.font = .systemFont(ofSize: gMenuTextSize, weight: .regular)
config.image = UIImage(systemName: "gearshape", withConfiguration: imageConfig)
config.imagePlacement = .top
config.titleAlignment = .center
config.imagePadding = 6
config.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
return config
}
}
This is on an approved app that has an in-app purchase. It was released with hosting turned off which is correct. I did a rev to the app. In the rev I mindlessly changed the in-app purchase and turned hosting on. I thought I was working on a different app. Anyway, I have tried to set it back and turn hosting off but it won't let me save. Now, on the same IAP, I have two status. I thought about deleting the existing IAP and creating a new one but the option to delete is not available. I thought about adding a new IAP and using it but that does not get rid of the Waiting for Upload IAP. Has anybody seen something like this and know how to resolve it. Is there a way to get someone with system level authority to help? Thanks for the help in advance.
I have a textField which opens an alert. The textField is displayed in a custom popup.
The textFiled is hooked to Editing_Did_End, Editing_Did_Begin and Editing_Changed actions.
I'm running Xcode 13.4.1
In iOS 15.5 everything works fine.
In iOS 15.6.1 the Editing_Did_End action is fired as soon as the alert opens. This is causing me a lot of trouble.
In iOS 15.5 the Editing_Did_End action is not fired when the alert opens. This is how it has worked in the past.
I'm pretty sure this is a bug.
Has anybody else run into this?
I'd be happy to share my code but the thing is that it works perfectly in iOS 15.5 so I don't think there's a problem with my code.