Using the Same Currency Formatting in Multiple Methods - swift

I'm using some currency formatting to set currency symbols/styles to the user's local settings. Here is my code. I think it works fine. It is located in my viewDidLoad.
let currencyFormat = NumberFormatter()
currencyFormat.locale = NSLocale.current
currencyFormat.usesGroupingSeparator = true
currencyFormat.numberStyle = NumberFormatter.Style.currency
...
labelTotalAmount.text = currencyFormat.string(for: totalAmount)
The trouble is, I want to use this same formatting in two other different Methods. It seems to be a waste to repeat the formatting for each method whenever I want to do formatting.
Is there a way to set the formatting once and have it remember the settings in every method of the class?
I'm using Swift 3. I appreciate any help you can give!!

Make it a computed property of the class:
class Whatever {
let currencyFormat: NumberFormatter = {
let res = NumberFormatter()
res.numberStyle = .currency
res.usesGroupingSeparator = true
return res
}()
}
Now you can use that property in any method.
func someMethod() {
let str = currencyFormat.string(for: 5.99)
}

Related

swift uidatePicker pass as arguements not working properly

i am stuck in situation i have to pass a uidatepicker to a cell class. the cell class contains 14 textfields and datepicker will be used as inputview to select to and from time for whole week. the thing is when i directly initialize it in cell class. it gets initialized 14 times and thats too much costly task. but while passing as arguement it replicate the selected time to all the textfield instead of that specific one. i tried to use tags and some other ways as well but no luck. how to handle the situation that it will not impact applicatio performancee .any help regarding this will be appriciated.thankyou
Code:
lazy var fromDatePicker: UIDatePicker = {
let fromDatePicker = UIDatePicker()
fromDatePicker.datePickerMode = .time
fromDatePicker.locale = NSLocale(localeIdentifier: "en_US") as Locale
fromDatePicker.minuteInterval = 30
if #available(iOS 13.4, *) {
fromDatePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
return fromDatePicker
}()
cell.configureCellForEmployee(with:viewModel.profileDataSource, index: indexPath.row, fromDatePicker: fromDatePicker)
Try adjusting the code.
class "you class": UIViewController{
lazy var fromDatePicker: UIDatePicker = {
let fromDatePicker = UIDatePicker()
fromDatePicker.datePickerMode = .time
fromDatePicker.locale = NSLocale(localeIdentifier: "en_US") as Locale
fromDatePicker.minuteInterval = 30
if #available(iOS 13.4, *) {
fromDatePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
return fromDatePicker
}()
}

Swift: Add comma and $ in the textfield when typing

I want the users see the $ and comma when they are typing in a textfield from numberPad (Without a decimal dot). Previously I got help and use below codes. But clients will not type the decimal (Only Int value).
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let oldText = textField.text! as NSString
var newText = oldText.stringByReplacingCharactersInRange(range, withString: string) as NSString!
var newTextString = String(newText)
let digits = NSCharacterSet.decimalDigitCharacterSet()
var digitText = ""
for c in newTextString.unicodeScalars {
if digits.longCharacterIsMember(c.value) {
digitText.append(c)
}
}
let formatter = NSNumberFormatter()
// formatter.usesSignificantDigits = false
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
formatter.locale = NSLocale(localeIdentifier: "en_US")
let numberFromField = (NSString(string: digitText).doubleValue) / 100
newText = formatter.stringFromNumber(numberFromField)
textField.text = String(newText)
return false
}
When typing, it always starts from the second decimal unit, How to remove the ".00" via editing the code and let it start from the unit? I tried for a long time, thanks in advance.
First, you got some bad advice. You should not be using shouldChangeCharactersInRange to change the characters in a text field. That's for checking if the characters typed are valid for the field. The only thing you should do in this method is return true if the user entered digits or delete, otherwise false. (Remember, the user may be using an external keyboard so just having the keypad up isn't good enough to stop non-digit entry.)
Instead you should be using an #IBAction connected to the field's EditingChanged event. Inside this method is where you should update the text.
#IBAction func editingChanged(sender: UITextField) {
let digits = sender.text?.digitsOnly ?? "0"
sender.text = "$\(digits).00" // If I understand what you want.
}
The below extension should be somewhere in your code base. It's generally useful so store it in a gist or something, you will likely need it in future projects.
extension String {
var digitsOnly: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).joinWithSeparator("")
}
}
You have to make sure that the IBAction is attached to the EditingChanged event:
For Swift 4x
extension String {
var digitsOnly: String {
return components(separatedBy: NSCharacterSet.decimalDigits.inverted).joined(separator: "")
}
}

swift + OS X sandboxing: treat 'NSVBOpenPanel' as a 'NSOpenPanel' :: because I need to get the sender in the delegate method

Im using swift and I show a NSOpenPanel. In the delegate I need to look at the sender's prompt to distinguish which action to take:
e.g.
func show() {
...
panel.delegate = self
panel.prompt = "xy"
panel.run ....
}
func show2() {
...
panel.delegate = self
panel.prompt = "abc"
panel.run ....
}
//delegate
func panel(sender: AnyObject, shouldEnableURL url: NSURL) -> Bool {
let panelPrompt = (sender as! NSOpenPanel).prompt ...
}
without sandbox = WORKS fine
the sender of the delegate is a NSOpenPanel indeed
with sandbox = Cast fails, crash
the sender of the delegate is NOT a NSOpenPanel but a NSVBOpenPanel. Apple's private class that remotely speaks to the outside world and allows the user to choose files NORMALLY not in your sandbox. (for details I refer to apple's sandboxing guide)
So the question is how do I do use this in swift without crashing?
Is there a nice way or is it just a bug/ugly idk behavior
Do I have to revert to use performSelector?
===
Addition: extensions to NSOpenPanel don't work either!
Instead of casting the sender to NSOpenPanel (which fails because the
sender is an instance of the private NSVBOpenPanel class),
or some performSelector magic, you can use the fact that
arbitrary methods and properties can be accessed on AnyObject
without casting, and the call behaves like an implicitly
unwrapped optional:
func panel(sender: AnyObject, shouldEnableURL url: NSURL) -> Bool {
let panelPrompt = sender.prompt ?? ""
// ...
return true
}
This gives the prompt for any sender object which has a prompt
property, and the empty string as a fallback. In my test it worked well
in a sandboxed environment.
See The strange behaviour of Swift's AnyObject for more details, examples, and references to the
documentation.
This is how it would work with performSelector. It is quite ugly though:
let panelPromptUnmanaged = (sender as! NSObject).performSelector(NSSelectorFromString("prompt"))
let panelPrompt = panelPromptUnmanaged != nil ? panelPromptUnmanaged.takeRetainedValue() as! String : ""

Swift - Declare a global NSDate without value

I want to create a global NSDate in my app, because I want to save the date of the first time ever the app has been opened in this variable. I wrote
"var LaunchDate = NSDate()" before the declaration of the main class of the first view controller, and in the viewdidload, if it is the first time the app is opened, it saves the date into the global variable LaunchDate. But everytime I open the app, it saves the current date because of "var LaunchDate = NSDate()".
I didn't find a solution, do you have any idea to declare a global date without that he gives the current date please?
You could use NSUserDefaults to store the value.
The code checks if a value exists.
If yes it reads the value, if no it writes the current date.
var launchDate : NSDate!
let defaults = NSUserDefaults.standardUserDefaults()
if let dateOfFirstLaunch = defaults.objectForKey("dateOfFirstLaunch") as? NSDate {
launchDate = dateOfFirstLaunch
} else {
let currentDate = NSDate()
defaults.setObject(currentDate, forKey:"dateOfFirstLaunch")
launchDate = currentDate
}
Global variables are variables that are defined outside of any
function, method, closure, or type context
struct globalDate
{
static var LaunchDate = NSDate()
}
In swift if you encapsulate the variable in struct, you can access that in any classes.
Document
Global Variable
Try this:
override func viewDidLoad() {
var LaunchDate: NSDate
// when you want to set the value for it:
LaunchDate = NSDate()
}
The Proble, is that NSDate() which you tried to do is a function, which gets back a value of the current date.
I would use the Singleton Pattern with NSUserDefaults.
The code should be something like this
import Foundation
class UserDefaults {
static let sharedInstance = UserDefaults()
var launchDate: NSDate? {
get {
return NSUserDefaults.standardUserDefaults().objectForKey("launch_date") as? NSDate
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "launch_date")
}
}
}
Access it using this code
UserDefaults.sharedInstance.launchDate

MKLocalSearch on Swift init() doesn't work

i am trying to use MKLocalSearch Api in Swift. But I can't get it to work. The error is coming from
var search:MKLocalSearch = MKLocalSearch.init(request)
I read the documentation, and it state the method name is init(request:) I am not sure what i did wrong. please advice. :)
var request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchTextFiled.text
println(searchTextFiled.text)
request.region = self.mapView.region //need to define region later
var search:MKLocalSearch = MKLocalSearch.init(request)
search.startWithCompletionHandler {
(response:MKLocalSearchResponse!, error:NSError!) in
if !error {
var placemarks:NSMutableArray = NSMutableArray()
for item in response.mapItems {
placemarks.addObject(placemarks)
}
self.mapView.removeAnnotations(self.mapView.annotations)
self.mapView.showAnnotations(placemarks, animated: true)
} else {
}
}
Change this line:
var search:MKLocalSearch = MKLocalSearch.init(request)
To:
var search:MKLocalSearch = MKLocalSearch.init(request: request)
It's made a little confusing by the naming involved in this case - the first part, request: is a label that tells Swift which init function to call - in this case, it's mapping back to the Objective-C initWithRequest: method.
The second part, request is your MKLocalSearchRequest variable that you're passing to the init function. So the Objective-C equivalent is this:
MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
There's a whole chapter titled "Initialization" in Apple's The Swift Programming Language book, available on the iBooks store and as a PDF here, albeit with worse (bordering on terrible) formatting in the PDF.
That's incorrect syntax for init. They're implied by Swift, so you'd use
var x = MKLocalSearch(request: request)
not
var x = MKLocalSearch.init(request)