2024년이 다 다가와서 주간회고록 블로그의 문서를 한 번 정리했다.
문서번호와 Format이 다른 부분들을 수정하고 내가 Ignite 저장소에 기여한 ContentPreView Style 설정하는 코드 PR이 승인이되서 코드를 적용해봤다.
비동기 작업을 구현하기 위해서 completionHandler를 이용하거나, async & awiat 문법을 사용할 수 있습니다.
비동기 작업을 시작한 후 완료가 되기전에 작업을 취소하고 싶은 경우 어떻게 각각 구현하는 지 알아보았습니다.
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()
// 6초가 지나도 실행되지 않음
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()
// 6초가 지나도 실행되지 않음
work.stopWork()
지지난 주 UITextField에 복사-붙여넣기 작업시 붙여넣기가 동작되도록 작업을 해보았는데 작업할 때는 숫자만 추출하여 붙여넣기가 불가능한 것으로 알았는데 다른 서비스에서 가능한 것으로 보아서 한 번 살펴보았습니다.
override func paste(_ sender: Any?)
함수를 통해서 붙여넣기 시점에 처리하는 함수가 있어서 해당 함수를 활용하여 해결했습니다.
또 추가적으로 알게된 사실은 iOS 16이상부터는 붙여넣기 방지를 사용자에게 물어볼 수 있습니다.
ex) 전화번호 유효성검사 붙여넣기
//
// 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 // 갯수 초과
}
// 최대 전화번호 길이 (예: 010-1234-5678)
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
}
}
// MARK: - 유효성 검증 로직
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: // 3자리 이하
return digits
case 4...6: // 가운데 번호가 3자리
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
}
}