As 2024 is coming to an end, I organized the weekly retrospective blog documents.
I fixed discrepancies in document numbers and formats and applied the code for ContentPreview style settings that I contributed to the Ignite repository after my PR was approved.
To implement asynchronous tasks, you can use either a completionHandler
or the async & await
syntax.
I explored how to cancel a task after it starts but before it completes using each method.
class AsyncWork {
var item: DispatchWorkItem?
func startWork() {
if item == nil {
item = DispatchWorkItem {
self.item = nil
}
DispatchQueue.main.asyncAfter(deadline: .now() + 6, execute: item!)
}
func stopWork() {
item.cancel()
itme.nil
}
}
let work = AsyncWork()
work.startWork()
work.stopWork()
class AsyncWork {
var item: Task?
func startWork() {
if item == nil {
item = Task { [weak self] in
guard let self else { return }
do {
try await Task.sleep(nanoseconds: 6_000_000_000)
self.item = nil
} catch {
self.item = nil
}
}
}
func stopWork() {
item?.cancel()
itme.nil
}
}
let work = AsyncWork()
work.startWork()
work.stopWork()
Two weeks ago, I worked on enabling paste functionality in a UITextField
. While working, I thought it was impossible to extract only numbers for pasting. However, seeing it work in another service made me take another look.
I found that the override func paste(_ sender: Any?)
function can be used to handle processing at the moment of pasting, so I used this function to resolve the issue.
Additionally, I learned that starting from iOS 16, the system can prompt the user before preventing paste operations.
Example: Phone number validation during pasting
//
// PhoneNumberTextField.swift
// SookimIosDesignSystem
//
// Created by sookim on 12/19/24.
//
import UIKit
protocol PhoneNumberTextFieldDelegate: AnyObject {
func editPhoneNumberText(result: PhoneNumberTextField.ResultDescription)
}
class PhoneNumberTextField: UITextField {
enum ResultDescription {
case success
case invalidPhoneNumber
case emptyPhoneNumber
case countLimit
}
private let maxPhoneNumberLength = 13
var onValidationResult: ((ResultDescription) -> Void)?
weak var phoneNumberTextFieldDelegate: PhoneNumberTextFieldDelegate?
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.keyboardType = .numberPad
self.delegate = self
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.translatesAutoresizingMaskIntoConstraints = false
self.keyboardType = .numberPad
self.delegate = self
}
override func paste(_ sender: Any?) {
if let pasteboardString = UIPasteboard.general.string {
let filteredString = filterAndFormatPhoneNumber(pasteboardString)
self.text = filteredString
let validationResult = validatePhoneNumber(filteredString)
onValidationResult?(validationResult)
phoneNumberTextFieldDelegate?.editPhoneNumberText(result: validationResult)
}
}
}
// MARK: - UITextFieldDelegate
extension PhoneNumberTextField: UITextFieldDelegate {
func textFieldShouldClear(_ textField: UITextField) -> Bool {
onValidationResult?(.emptyPhoneNumber)
phoneNumberTextFieldDelegate?.editPhoneNumberText(result: .emptyPhoneNumber)
return true
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let currentText = textField.text as NSString? else { return true }
let newText = currentText.replacingCharacters(in: range, with: string)
let filteredText = filterAndFormatPhoneNumber(newText)
if filteredText.count > maxPhoneNumberLength {
onValidationResult?(.countLimit)
phoneNumberTextFieldDelegate?.editPhoneNumberText(result: .countLimit)
return false
}
textField.text = filteredText
let validationResult = validatePhoneNumber(filteredText)
onValidationResult?(validationResult)
phoneNumberTextFieldDelegate?.editPhoneNumberText(result: validationResult)
return false
}
}
extension PhoneNumberTextField {
private func filterAndFormatPhoneNumber(_ input: String) -> String {
let digitsOnly = input.compactMap { $0.isNumber ? String($0) : nil }.joined()
let formattedNumber = formatAsPhoneNumber(digitsOnly)
return formattedNumber
}
private func formatAsPhoneNumber(_ digits: String) -> String {
let length = digits.count
switch length {
case 0...3:
return digits
case 4...6:
let firstIndex = digits.index(digits.startIndex, offsetBy: 3)
return "\(digits[.. ResultDescription {
if text.isEmpty {
return .emptyPhoneNumber
}
if text.count > maxPhoneNumberLength {
return .countLimit
}
if !text.starts(with: "010") || text.count < (maxPhoneNumberLength - 1) {
return .invalidPhoneNumber
}
return .success
}
}