How to copy Objects in Swift

I wanted to populate an array with multiple unique copies of an existing object. If you just append the same object in a loop, you just end up with an array with multiple references to the one object!

I thought that there would be a simple way to copy the instance of an object in Swift. I was wrong! The simplest way is to use copy() which requires the following three steps:

  1. Make your class conform to NSCopying. This isn’t strictly required, but it makes your intent clear.
  2. Implement the method copy(with:), where the actual copying happens.
  3. Call copy() on your object.

See for more details…


How to add a ‘Done’ button to a numberPad in Swift

The best way to add a ‘Done‘ button to a numberPad, which doesn’t have it by default,  is to extend UITextField. Three steps:

1. set the keyboard type for your UITextField in your viewController subclass:

e.g. myTextField.keyboardType = UIKeyboardType.numberPad

2. save the code below the line as  ‘UITextField+Extensions.swift’ and add to your project.

3. After this, if you select any UITextField in the storyboard editor, there will be a new item under the ‘Attributes Inspector’ for that UITextField, where you can set ‘Done Access…’ On, Off or default. Choose ‘On’ if you want the ‘Done’ button to show up.

import Foundation

import UIKit

extension UITextField{

       @IBInspectable var doneAccessory: Bool{


            return self.doneAccessory


        set (hasDone) {

            if hasDone{






    func addDoneButtonOnKeyboard()


        let doneToolbar: UIToolbar = UIToolbar(frame: CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))

        doneToolbar.barStyle = .default


        let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)

        let done: UIBarButtonItem = UIBarButtonItem(title: “Done”, style: .done, target: self, action: #selector(self.doneButtonAction))


        let items = [flexSpace, done]

        doneToolbar.items = items



        self.inputAccessoryView = doneToolbar



    @objc func doneButtonAction()





how to do validation of a UITextField, while still being edited

If you want to add validation to an instance of UITextField, while editing is still going on, there are a few things you need to do.

Step 1:

Make the viewController adopts UITextFieldDelegate in the class declaration

e.g. class myViewController: UIViewController, UITextFieldDelegate {…}

Step 2:

Set the viewController as the delegate for each of your UITextFields, e.g. in the viewController’s viewDidLoad() function. ‘self’ refers to the viewController.

e.g. myTextField.delegate = self

Step 3:

You need to include the following optional delegate function within your viewController class:

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {…}

The important thing to note is that you can’t just check if the UITextField.text is nil or empty when unwrapped, because that won’t work if the UITextField is currently being edited. For that you need to check the UITextField.text + the replacementString.


Note: Since UITextField.text can be nil, safer to set a variable using the nil coalescing operator, then concatonate with the string

e.g. let currentTextInTextField = textField.text ?? “”

then you can do your validation against (currentTextInTextField+string)

Step 4:

return false if your validation fails, or true if it succeeds.

iPhone / Apple Watch and EZ-Link, NETS Flashpay

Since my iPhone reacts when brought near to the NFC (near-field communication) terminal on the bus and brings up the “Pay with Touch ID” screen, I was wondering if you could use your iPhone instead of an EZ-Link or NETS Flashpay card to pay for a bus or train trip.

SMRT and SBS Transit support the EZ-Link and NETS Flashpay contactless cards for payment on buses and trains here in Singapore.

Since a few years ago both Android and iPhone handsets have started to include an NFC chip. I believe that there are android apps that let you use your android phone as an EZ-Link / NETS Flashpay card.

As far as the iPhone is concerned, Apple added some NFC support in iOS11 – it should now be possible to use an iPhone to *read* an NFC card. As far as using an iPhone with an NFC terminal however, they restrict that functionality to only support Apple Pay – that’s why you can’t currently use your iPhone to pay for a trip on the bus or train.

update – I heard that Apple do support the Suica transit card in Japan.

Let’s hope they start extending the support of transit cards to other countries – including Singapore…


How to add your Starbucks Card to Apple Watch (if ‘Wallet’ isn’t an option)

Even though there’s no Starbucks app for Apple Watch here in Singapore, and no option for adding your Starbucks card to the ‘Wallet’, you can still use your Apple Watch as your Starbucks Card. Here’s how:

  1. open your Starbucks app on the iPhone.
  2. click on ‘Pay’ at the bottom. You will then see a screen showing your card’s barcode
  3. Do a screen-shot by pressing the home button and power button at the same time
  4. Tap on the small image in the bottom left to bring up the image editor
  5. Crop the image to only have the barcode showing, then tap ‘Done’ and chose ‘Save to Photos’.
  6. Open the barcode image in the Photos app. tap on ‘Edit’. Tap on the ‘rotate and crop’ button at the bottom. Then you’ll see a ‘rotate by 90 degrees’ button show up. Tap that twice to rotate the barcode image by 180 degrees.
  7. Tap ‘Done’.
  8. Create a new Album and call it ‘Starbucks’. Add that image to the album.
  9. Open the ‘watch’ app on the iPhone and under ‘Synced Album’ choose ‘Starbucks’. The image will then get copied over to the Apple Watch.
  10. When you want to pay at Starbucks using the watch, open the ‘Photos’ app on the watch. Turn your wrist so that the screen points to the barcode reader and press the crown or touch the screen so that the image shows on the watch.

Update: This has worked for me five times so far, at different outlets. Has only failed once – where the barcode scanner was in an odd position…