반응형

두가지 모두 비동기 프로그래밍을 위해 사용되는 기술로, 멀티 스레딩 및 동시성 처리를 할때 사용된다.

두 기술에 대한 비교는 다음과 같다.

 

  1. 비동기 작업 처리:
    • 두 기술 모두 비동기적으로 작업을 수행할 수 있다. 이를 통해 메인 스레드가 블로킹되지 않으며, 사용자 인터페이스가 멈추지 않게 할 수 있다.
  2. 멀티 스레딩 지원:
    • GCD는 작업을 비동기적으로 다양한 큐에서 실행하고, Swift Concurrency는 태스크를 사용하여 비동기 코드를 스케줄링한다.
    • 둘 다 멀티코어 CPU의 성능을 활용해 병렬 처리가 가능하다.
  3. 스레드 관리 자동화:
    • GCD는 스레드 풀을 관리하여 시스템 리소스를 효율적으로 사용한다.
    • Swift Concurrency도 내부적으로 Task Scheduler를 사용하여 스레드 관리와 태스크 스케줄링을 자동으로 최적화한다.
  4. QoS (Quality of Service):
    • 두 기술 모두 작업의 우선 순위를 지정할 수 있습니다.
      • GCD에서는 DispatchQoS를 사용해 작업 우선 순위를 설정한다.
      • Swift Concurrency에서는 Task의 우선 순위를 조정할 수 있다 (Task(priority: .high) 등).
  5. 캔슬 가능한 작업:
    • GCD에서 작업을 수동으로 취소할 수 있는 것은 아니지만, Swift Concurrency에서는 Task를 취소할 수 있다. 다만, GCD에서도 DispatchWorkItem을 사용하여 작업을 취소할 수 있는 형태로 구현할 수 있다.
    • Swift Concurrency에서는 Task.checkCancellation() 등을 사용해 비동기 태스크 내에서 취소 상태를 확인할 수 있다.

 

요약

공통점 GCD (Grand Central Dispatch) Swift Concurrency (Async/Await)
비동기 작업 처리 DispatchQueue.async {} async/await, Task
멀티 스레딩 지원 다양한 DispatchQueue Task 스케줄러
스레드 관리 자동화 자동으로 스레드 풀 관리 Task 스케줄러로 자동 최적화
우선 순위 제어 DispatchQoS Task(priority:)
작업 취소 지원 DispatchWorkItem (부분적으로 지원) Task.cancel(), Task.checkCancellation()

 

특징 GCD (Grand Central Dispatch) Swift Concurrency (Async/Await)
개념 스레드 풀을 사용해 작업을 비동기적으로 처리하기 위한 API. Swift 5.5에서 도입된 구조화된 비동기 프로그래밍 모델.
도입 시기 iOS 4 iOS 15, macOS 12
사용 방식 큐를 사용해 작업을 비동기적으로 추가하고, 콜백을 사용해 결과 처리. async/await 키워드를 사용해 코드 흐름을 동기적으로 작성.
코드 가독성 콜백 지옥 (Callback Hell) 발생 가능 코드 흐름이 동기 코드와 유사하여 가독성이 높음
성능 최적화 스레드 풀 관리, 우선순위 조정 등 수동으로 최적화 가능 시스템이 자동으로 최적화 (더 나은 스케줄링)
에러 처리 콜백 내에서 수동으로 에러 처리 do-catch 블록을 통한 구조화된 에러 처리
캔슬레이션 지원 수동으로 작업 취소 관리 Task 객체를 통한 내장된 캔슬레이션 지원
메모리 관리 클로저 캡처에 주의해야 하며, weak self를 사용해 메모리 누수 방지 async/await는 메모리 안전성을 자동으로 보장
주요 사용 사례 레거시 코드, iOS 14 이하 호환성 최신 iOS 프로젝트, 비동기 코드 리팩토링

 

GCD (Grand Central Dispatch) 샘플 코드

import UIKit

func fetchDataWithGCD() {
    let url = URL(string: "https://www.apple.com")!
    
    // 네트워크 요청을 백그라운드에서 수행하고, 메인 스레드에서 UI 업데이트를 수행
    DispatchQueue.global(qos: .userInitiated).async {
        // 클로저 내부에서 self를 캡처하기 때문에 메모리 누수 방지를 위해 [weak self]를 사용해야 한다.
        guard let data = try? Data(contentsOf: url) else { return }
        DispatchQueue.main.async {
            print("Data fetched: \(data)")
        }
    }
}

fetchDataWithGCD()

 

Swift Concurrency (Async/Await) 샘플 코드

import Foundation

func fetchData() async {
    let url = URL(string: "https://www.apple.com")!
    
    do {
        // async/await를 사용해 네트워크 요청을 수행하고 결과를 처리 (비동기 방식)
        let (data, _) = try await URLSession.shared.data(from: url)
        print("Data fetched: \(data)")
    } catch {
        print("Error fetching data: \(error)")
    }
}

// Task를 생성하여 비동기 함수 호출
Task {
    await fetchData()
}

 

iOS 15 이상을 타겟으로 하는 프로젝트에서는 Swift Concurrency (async/await)를 사용하는 것이 코드 가독성, 자동 메모리 관리, 에러 처리 등에서 GCD에 비해 이점이 있다.

 

반응형
블로그 이미지

SKY STORY

,
반응형

 

  1. 사진 앱 개편: 사진 앱은 새로운 레이아웃으로 개선되었으며, '컬렉션' 기능을 통해 주제별로 사진을 쉽게 탐색할 수 있습니다. 검색 기능도 강화되어, 날짜, 장소, 인물 등 다양한 기준으로 사진을 찾을 수 있습니다.
  2. 비밀번호 앱 도입: iOS 18에서 처음으로 비밀번호 관리 전용 앱이 추가되어, 비밀번호, 패스키, Wi-Fi 비밀번호 등을 한 곳에서 손쉽게 관리할 수 있게 되었습니다.
  3. 사파리 개선: '하이라이트' 기능이 도입되어 웹페이지에서 중요한 정보를 쉽게 확인할 수 있으며, '산만함 제어' 기능을 통해 쾌적한 브라우징 환경을 제공합니다. 또한, 강화된 추적 방지 기능으로 광고 추적이 줄어듭니다.
  4. 게임 성능 향상: '게임 모드'가 추가되어 배경 활동을 최소화하고 더 높은 프레임률을 유지합니다. AirPods와 무선 컨트롤러의 응답 속도도 개선되어 보다 향상된 게임 경험을 제공합니다.
  5. Apple Wallet 개선: 'Tap to Cash' 기능이 추가되어 다른 iPhone 사용자에게 돈을 쉽게 보낼 수 있으며, 이벤트 티켓 디자인도 새롭게 개선되었습니다. 이는 사용자의 결제 편의성을 높이는 데 기여합니다.
  6. 개인정보 보호 강화: Face ID, Touch ID 또는 비밀번호로 앱을 잠글 수 있는 기능이 추가되어 보안이 한층 강화되었습니다. 또한, 숨길 수 있는 옵션이 제공되어 사용자의 프라이버시를 보호합니다.
  7. NFC 기능 확장: iOS 18.1에서는 NFC API 접근이 개발자에게 개방되어, Apple Pay와는 별도로 앱 내에서 NFC를 활용한 결제 시스템을 구현할 수 있습니다. 이로 인해 차량 키, 학생증, 호텔 키, 멤버십 카드 등 다양한 용도로 NFC를 활용할 수 있게 됩니다.
    • 개발자가 이 기능을 활용하려면 Apple과 상업적 계약을 체결하고 NFC 및 Secure Element(Secure Element, SE) API의 사용 권한을 요청해야 하며, 이는 보안과 규제를 준수하도록 설계되어 있습니다​.
    • NFC 기능은 매장 결제, 자동차 키, 교통 카드, 기업 배지, 학생증 등 다양한 분야에서 활용될 수 있으며, 향후 정부 ID 지원도 계획되어 있습니다​.
    • iOS 18.1 업데이트를 통해 개발자들이 Apple Pay 및 Wallet에 의존하지 않고도 자신의 앱 내에서 NFC 결제를 통합할 수 있는 기능이 추가됩니다.
  8. 애플 AI 기능: iOS 18.1 버전부터 "Apple Intelligence"라는 AI 기반 기능이 도입됩니다. 주요 기능은 다음과 같습니다:
    • 작성 도구: 텍스트를 교정, 재작성, 재구성하는 기능을 제공합니다.
    • 사진 정리: 사진에서 원치 않는 객체를 쉽게 제거할 수 있는 기능이 추가되었습니다.
    • 기억 영화 생성: 설명을 입력하면 AI가 자동으로 기억 영화를 생성합니다.
    • 자연어 검색: 사진과 비디오를 더 효율적으로 검색할 수 있는 기능이 도입되었습니다.
    • 메일 및 메시지 요약: 이메일과 메시지의 중요한 내용을 요약해주는 기능이 추가되었습니다.
    • 스마트 응답: 메일과 메시지에 대해 더욱 향상된 제안된 답변을 제공합니다.
    • Siri 향상: 더 자연스러운 목소리와 사용하기 쉬운 인터페이스를 갖춘 Siri의 첫 번째 버전이 도입되었습니다.

 

 

 

반응형
블로그 이미지

SKY STORY

,
반응형

앱스토어 배포를 위해 리뷰 전 'Validate App' 하면 아래 그림과 같이 NDEF 에러가 발생하는 경우가 있다. 이 해결방법을 알아보도록 하겠다.

 

1. Info.plist에 NFC관련 권한이 설정되어있는지 확인하자. 

<key>NFCReaderUsageDescription</key>
<string>This app would like to use NFC for some reason.</string>

 

2. 해당  앱의 App ID 설정에 'NFC Tage Reading'이 체크되어있는지 확인한다.

 

3. .entitlements 파일에 TAG설정이 되어있는지 확인하고 NDEF 부분은 제거한다.

<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  <plist version="1.0">
  <dict>
      <key>com.apple.developer.nfc.readersession.formats</key>
      <array>
          <string>TAG</string>
          <!-- 'NDEF는 제거하도록 한다. (대부분 이부분 때문에 에러 발생됨)
          <string>NDEF</string>
          -->
      </array>
  </dict>
  </plist>

 

4. Clean & ReBuild

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
블로그 이미지

SKY STORY

,
반응형

UITextField, UITextView 입력 시 키보드가 활성화될 때 키보드에 가려지지 않도록 하는 방법을 알아보도록 한다.

아래 샘플은 UITableView에서 입력 시 키보드에 가려지지 않도록 처리하는 예제이다.

class ViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet weak var tableView: UITableView!

    private var activeTextField: UITextField?
    private var activeTextView: UITextView?

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        registerForKeyboardNotifications()
    }
	... 중간 생략 ...
    
    
    // 활성화된 TextView 입력 필드를 추적 및 갱신
    func textFieldDidBeginEditing(_ textField: UITextField) {
        activeTextField = textField
        activeTextView = nil
    }
    
    func textFieldDidEndEditing(_ textField: UITextField) {
        activeTextField = nil
    }
    
    
	// 활성화된 TextView 입력 필드를 추적 및 갱신
    func textViewDidBeginEditing(_ textView: UITextView) {
        activeTextView = textView
        activeTextField = nil
    }
    
    func textViewDidEndEditing(_ textView: UITextView) {
        activeTextView = nil
    }
}

extension ViewController {
	// 키보드가 나타날 때, 키보드가 내려갈 때 이벤트 등록
    func registerForKeyboardNotifications() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(_:)), name: UIResponder.keyboardDidShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
	
    // 키보드가 나타날 때 실행되며, 현재 활성화된 입력 필드가 키보드에 의해 가려지지 않도록 스크롤
    @objc func keyboardWasShown(_ notification: NSNotification) {
        guard let userInfo = notification.userInfo,
              let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }

        let keyboardRectangle = keyboardFrame.cgRectValue
        let keyboardHeight = keyboardRectangle.height

        let insets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
        tableView.contentInset = insets
        tableView.scrollIndicatorInsets = insets

        var aRect = self.view.frame
        aRect.size.height -= keyboardHeight

        if let activeTextField = activeTextField, let textFieldRect = activeTextField.superview?.convert(activeTextField.frame, to: tableView) {
            if !aRect.contains(textFieldRect.origin) {
                tableView.scrollRectToVisible(textFieldRect, animated: true)
            }
        } else if let activeTextView = activeTextView, let textViewRect = activeTextView.superview?.convert(activeTextView.frame, to: tableView) {
            if !aRect.contains(textViewRect.origin) {
                tableView.scrollRectToVisible(textViewRect, animated: true)
            }
        }
    }
	
    // 키보드가 내려갈 때 
    @objc func keyboardWillBeHidden(_ notification: NSNotification) {
        tableView.contentInset = .zero
        tableView.scrollIndicatorInsets = .zero
    }
}

 

반응형
블로그 이미지

SKY STORY

,
반응형

UITextField 컨트럴을 사용하여 숫자를 입력 받을 경우 입력된 숫자에 자동으로 콤마를 추가되도록 구현해 보았다. 또한 숫자 이외의 문자입력 방지 및 Int형 변환처리등을 구현하였다.

import Foundation
import UIKit

class MainViewController: UIViewController {

    @IBOutlet weak var tfAmount: UITextField!
    
    override func viewDidLoad() {
    	super.viewDidLoad()
        
        // 키보드 타입 설정 (storyboard에서 설정 가능)
        tfAmount.delegate = self
        tfAmount.keyboardType = .numbersAndPunctuation  // 숫자와 구두점이 포함된 키보드로 설정
        tfAmount.clearButtonMode = .whileEditing        // 편집하는 동안 'clear' 버튼 활성화
        tfAmount.clearsOnBeginEditing = true            // 편집 시작 시 내용 지우기
    }
    
    import UIKit

extension MainViewController : UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if textField == self.tfJJRefundAmount {
            // 숫자 이외의 문자는 입력되지 않도록 처리
            let invalidCharacters = CharacterSet.decimalDigits.inverted
            return string.rangeOfCharacter(from: invalidCharacters) == nil
        }
        return true
    }
    
    func textFieldDidChangeSelection(_ textField: UITextField) {
        if textField == self.tfAmount {
            // 입력시 자동으로 ',' 추가
            if let text = textField.text?.replacingOccurrences(of: ",", with: "") {
                if let number = Int(text) {
                    let formatter = NumberFormatter()
                    formatter.numberStyle = .decimal
                    textField.text = formatter.string(from: NSNumber(value: number))
                    self.tfAmount.tag = number // Int형 값을 tag에 저장.
                }
            }
        }
    }

    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
        if textField == self.tfAmount && textField.text?.isEmpty == true {
            // 필요한 경우 추가 처리
        }
        return true
    }
    
    func textFieldShouldClear(_ textField: UITextField) -> Bool {
        textField.text = ""
        return true
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()    // 키보드 내리기
        
        if textField == self.tfAmount && textField.text?.isEmpty == true {
            // 필요한 경우 추가 처리
        }
        return true
    }
}

 

다음 코드는 'OK' 버튼을 선택했을 때 입력된 금액을 Int형 접근하는 사용 예이다.

extension MainViewController {
    
    @IBAction func okButtonTapped(_ sender: UIButton) {
	    guard let amountText = tfAmount.text else {
            print("금액 입력이 올바르지 않습니다.")
            return
        }
        
        // 금액 (Int형 사용을 위해 tag값 사용)
        var amount = self.tfAmount.tag 
        // 배송비
        var shippingCost = 3000 
        // 총 금액 계산
        let totalAmount = amount + shippingCost
        
        print("총 금액 : \(totalAmount)원")
    }
}

 

 

반응형
블로그 이미지

SKY STORY

,
반응형

뷰 화면 내에서 플로팅 및 드레깅 가능한 버튼을 만들어 보았다.

draggableView 사이즈는 50x50으로 설정하였다.

 

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var draggableView: UIView!
    
    var isStickyEffect: Bool! = false       // 좌우측으로 자동으로 벽에 붙도록 처리 flag
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 팬 제스처 및 탭 제스처 추가
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handler))
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(draggableViewTapped))
        
        draggableView.addGestureRecognizer(panGesture)
        draggableView.addGestureRecognizer(tapGesture)
    }
    
    @objc func handler(gesture: UIPanGestureRecognizer) {
        let location = gesture.location(in: self.view)
        let draggedView = gesture.view
        
        // 뷰의 반너비와 반높이 계산
        let halfWidth = draggedView!.bounds.width / 2
        let halfHeight = draggedView!.bounds.height / 2
        
        // Safe Area를 고려한 화면 경계에 도달하면 뷰를 화면 안쪽으로 이동시킴
        let minX = halfWidth
        let maxX = view.safeAreaLayoutGuide.layoutFrame.width - halfWidth
        let minY = view.safeAreaInsets.top + halfHeight
        let maxY = view.bounds.height - halfHeight
        
        // 좌표 제한
        draggedView?.center.x = max(minX, min(location.x, maxX))
        draggedView?.center.y = max(minY, min(location.y, maxY))
        
        // 드레그 상태 종료시 이벤트
        if gesture.state == .ended {
            stickyEffect()
        }
    }
}



extension ViewController {
    // 좌우측으로 자동으로 벽에 붙도록 처리
    func stickyEffect() {
        let halfWidth = draggableView.bounds.width / 2
        if isStickyEffect == true {
            // 이동이 끝났을 때 특별한 효과 없이 뷰가 그 자리에 고정됨
            if self.draggableView.frame.midX >= self.view.layer.frame.width / 2 {
                UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseIn, animations: {
                    self.draggableView.center.x = self.view.layer.frame.width - halfWidth//40
                }, completion: nil)
            } else {
                UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseIn, animations: {
                    self.draggableView.center.x = halfWidth//40
                }, completion: nil)
            }
        }
    }
    
    // 버튼 텝 이벤트
    @objc func draggableViewTapped() {
        let message = "버튼 텝 이벤트"
        let alertController = UIAlertController(title: "Alert", message: message, preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alertController.addAction(okAction)
        present(alertController, animated: true, completion: nil)
    }
}

 

 

 

2024.01.11 - [Note] - BLE, Beacon, iBeacon

2024.01.11 - [Note] - BLE Advertising Payload format 샘플 분석

2024.01.09 - [Note] - Packet Format for the LE Uncoded PHYs

2024.01.04 - [iOS] - Floating, Dragging Button

2023.12.27 - [Bark Bark] - 말이 통하지 않는 사람과 싸우지 말라.

2023.12.27 - [Server] - Push Notification

2023.12.27 - [AI,ML,ALGORITHM] - A star

2023.12.07 - [iOS] - XOR 연산을 이용한 문자열 비교

2023.11.03 - [AI,ML,ALGORITHM] - Gomoku(Five in a Row, Omok) (5/5) - 3x3 체크 추가

2023.10.29 - [AI,ML,ALGORITHM] - Gomoku(Five in a Row, Omok) (5/5) - 머신러닝으로 게임 구현

 

반응형
블로그 이미지

SKY STORY

,
반응형

일반적으로 문자열 비교는 '==' 연산자를 사용하거나 문자열 비교함수 'compare(_ :options:)'매서드를 사용한다.

그러나 위와 같이 결과 값이 True, False로 반환된 결과 값으로 처리할 경우 해당 결과 값을 변조하여 해당 조건문을 통과할 수 있다. 이러한 문제를 우회하기 위하여 아래와 같이 XOR 연산을 이용한 문자열 비교처리가 필요할 수 있다.

let str1 = "ABC123"
let str2 = "ABC123"

guard let utf8Bytes1 = str1.data(using: .utf8),
      let utf8Bytes2 = str2.data(using: .utf8) else {
    return
}

// 각각의 바이트 별로 xor한 결과값을 어레이로 반환
let xorResult = zip(utf8Bytes1, utf8Bytes2).map { $0.0 ^ $0.1 } // xor 비교

// xorResult의 모든 값이 0인지 체크 - 모두 0이어야 동일한 문자열 이다.
if (xorResult.allSatisfy { $0 == 0 }) {
    // 좀더 확실하게 막기 원한다면 다시한번 체크.
	for i in 0..<utf8Bytes1.count {
        if utf8Bytes1[i] ^ utf8Bytes2[i] != 0 {
            return
        }
    }
    
    // TODO : 작업은 여기에....
    
}

 

반응형

'개발 > iOS' 카테고리의 다른 글

UITextField 숫자 입력 시 콤마 추가 및 사용  (0) 2024.05.14
Floating, Dragging Button  (1) 2024.01.04
Rosetta2  (0) 2023.04.26
NFC tag read/write Manager Class (2/2)  (0) 2023.04.26
NFC tag read/write Manager Class (1/2)  (0) 2023.04.26
블로그 이미지

SKY STORY

,

Rosetta2

개발/iOS 2023. 4. 26. 14:21
반응형

애플 Silicon CPU사용 컴퓨터의 경우 인텔CPU 전용으로 개발된 앱을 실행하려면 

Rosetta2를 설치해야 한다. 콘솔창을 띄워 다음 명령을 실행해 준다.

 

// 1. 라이센스 동의 필요

softwareupdate —install-rosetta

A Enter

 

// 2. 라이선스 자동 동의

/usr/sbin/softwareupdate —install-rosetta —agree-to-license

반응형

'개발 > iOS' 카테고리의 다른 글

Floating, Dragging Button  (1) 2024.01.04
XOR 연산을 이용한 문자열 비교  (0) 2023.12.07
NFC tag read/write Manager Class (2/2)  (0) 2023.04.26
NFC tag read/write Manager Class (1/2)  (0) 2023.04.26
Carthage 설치 및 제거  (0) 2023.01.11
블로그 이미지

SKY STORY

,