CocoaPods: iOS 프로젝트의 필수 라이브러리 관리 도구

swift와는 직접적으로 연관되어 있지는 않지만, 카카오톡 소셜 로그인 기능을 구현하다가 필요해서 글을 남긴다.

CocoaPods는 iOS 및 macOS 개발에서 필수적인 라이브러리 관리 도구이다. 수천 개의 오픈소스 라이브러리를 손쉽게 프로젝트에 통합하고 관리할 수 있게 한다. (python에서의 pip라고 생각하면 좋을 지도?) 이번 포스팅에서는 Cocoapods의 개념, 설치 방법, 그리고 실제로 프로젝트에서 사용하는 방법을 자세히 설명해 보겠다.

 

1. CocoaPods란?

CocoaPods는 Xcode 프로젝트에 외부 라이브러리를 쉽게 통합하고 관리할 수 있게 해주는 의존성 관리 도구이다. 오픈 소스 라이브러리뿐만 아니라 사내에서 개발한 라이브러리도 쉽게 추가, 업데이트, 삭제할 수 있으며, 라이브러리 간의 의존성을 자동으로 해결해준다.

 

2. CocoaPods 설치

CocaPods를 사용하기 위해서는 먼저 설치가 필요하다. 설치는 RubyGems를 통해 간단하게 할 수 있다. 먼저 터미널을 열고, 다음 명령어로 CocoaPods를 설치한다. 만약 RubyGems 버전이 낮다면 버전을 올린 뒤 설치한다.

sudo gem install drb -v 버전
sudo gem install cocoapods

설치가 완료되면 CocoaPods 버전을 확인하여 제대로 설치되었는지 확인할 수 있다.

pod --version

 

3. CocoaPods 설정

CocoaPods를 설치하고 나서 Xcode 프로젝트에 설정하는 방법을 알아보자. 먼저 Xcode 프로젝트가 있는 디렉토리로 이동한다. 그리고 Podfile을 생성해준다. Podfile은 프로젝트에서 사용할 라이브러리 목록을 정의하는 파일로, bash에서

pod init

으로 생성할 수 있다. 꼭 /project 에서 생성해주자. 그리고 뭐든 편집기를 이용하여 Podfile을 수정해주자. Podfile을 열어 사용할 라이브러리를 추가한다. 나는 kakao developers에서 안내하는 대로 작성했다.

카카오 로그인을 위한 Podfile 설정
카카오 로그인을 위한 Podfile 설정

안내에는 이것저것 많이 알려줬지만, 사실 필요한건 KakaoSDKCommon과 KakaoSDKAuth, KakaoSDKUser 인듯하다. Podfile을 수정했다면, 다음 명령어를 실행하여 라이브러리를 설치하자.

pod install

이 명령어는 'Podfile'에 명시된 모든 라이브러리를 다운로드하고, Xcode 프로젝트를 위한 새로운 '.xcworkspace'파일을 생성한다. 앞으로는 '.xcodeproj'파일이 아니라, '.xcworkspace'파일을 사용해야 한다. 이 파일을 사용하여 Xcode에서 프로젝트를 열고 작업을 시작한다.

 

4. CocoaPods 사용 시 유의사항

Pod 업데이트: 라이브러리를 업데이트하려면 'Podfile'을 수정한 후, 'pod update'라는 명령어를 실행하여 업데이트 할 수 있다. 특정 라이브러리만 업데이트하려면 'pod update 라이브러리'와 같이 라이브러리 이름을 명시할 수 있다.

Pod 삭제: 사용하지 않는 라이브러리를 삭제하려면 'Podfile'에서 해당 라이브러리 항목을 제거한 후, 'pod install' 명령어를 실행한다.

Pod 버전 고정: 특정 버전의 라이브러리를 유지하려면 'Podfile'에서 버전을 명시해두는 것이 좋다. 그러면 업데이트 시에도 이 버전이 유지된다.

728x90

이전글: 2024.08.20 - [🔨 개발/📱 swift] - swift를 이용한 iOS 앱 개발 입문 3: 'IBOutlet'과 'IBActon'

UITextField를 사용해서 유저로부터 텍스트 입력을 받는 기능을 만들게 되었다. 아마 거의 모든 앱에서 사용될 기능이 아닐까 싶은데, 생각보다 간단하면서도 그와중에 고군분투의 흔적이 있어 기록으로 남겨본다.

 

1. storyboard에서 UITextField 생성하고 배치하기

Text Field를 storyboard에 추가
Text Field를 storyboard에 추가

필요한 UI 요소를 드래그앤드롭으로 storyboard에 추가하고 constraints를 설정하여 원하는 위치에 예쁘게 배치해준다. 이제는 기계적으로 하는 작업이 되어버렸다. 그리고 storyboard 옆에다 ViewController를 열어 control+드래그로 IBOutlet을 연결한다. IBOutlet의 사용방법을 모른다면 이전 글을 참고하자. commentText라는 변수명으로 ViewController 파일에서 댓글 텍스트 영역을 동적으로 접근하고자 한다.

@IBOutlet weak var commentText: UITextField! // 댓글 텍스트 영역


2. 각종 꾸미기 옵션들(borderWidth, borderColor, placeholder)

이제 commentText를 예쁘게 꾸며주는 몇 가지 작업들을 거쳐본다. viewDidLoad() 메서드 내부에 borderwidth, borderColor를 설정하는데, borderColor라는 변수는 휴대폰 기기의 설정이 다크 모드이면 흰색, 라이트 모드이면 검은색으로 주었다. 그리고 placeholder 역시 UITextField가 비활성화 된 상태와 활성화 된 상태를 다르게 설정하였다. 매번 isEnabled를 바꿔줄때마다 updateCommentPlaceholder() 메서드를 수동으로 호출해야 하는지는 의문이지만, 추후에 공부하기로 하고 일단은 번거로워도 그냥 호출한다.

@IBOutlet weak var commentDiv: UIView! // 댓글 영역
@IBOutlet weak var commentImg: UIImageView! // 댓글 흑백 아이콘
@IBOutlet weak var commentText: UITextField! // 댓글 텍스트 영역

override func viewDidLoad() {
	// 생략
    
    let borderColor = UIColor { traitCollection in
            return traitCollection.userInterfaceStyle == .dark ? UIColor.white : UIColor.black
        }
    
    // 댓글 텍스트 영역의 border 옵션 설정
    commentText.layer.borderWidth = 1.0
    commentText.layer.borderColor = borderColor.cgColor
    
    commentText.isEnabled = false // 최초에는 비활성화 된 상태로 시작
    self.updateCommentPlaceholder()
}

func updateCommentPlaceholder() {
    if commentText.isEnabled {
        commentText.placeholder = "00님의 생각을 공유해주세요."
    } else {
        commentText.placeholder = "선택지를 고른 후 댓글을 남길 수 있어요."
    }
}

이제 여기까지 왔으면 꽤 멀쩡해보이는 화면이 만들어 진 듯하다.

 

3. 가상 키보드 높이를 감지하여 위치 이동하기

UITextField에 별도의 IBAction을 지정하지 않아도 기본적으로 터치 시, 아이폰의 가상 키보드를 열어주는 역할을 하나보다. 그런데 문제는, 화면의 최하단(정확히 말하면 safearea의 최하단)에 고정되어 있는 UITextField가 가상 키보드가 나타나면서 같이 따라 올라오지 않아 눈에 보이지 않는다는 것이다. 이를 NotificationCenter를 이용해서 해결한 방법을 공유하고자 한다.

override func viewDidLoad() {
    super.viewDidLoad()

    // 생략

    // 댓글 영역 배경색 설정
    commentDiv.backgroundColor = UIColor { traitCollection in
            return traitCollection.userInterfaceStyle == .dark ? UIColor.black : UIColor.white
    }
    // 댓글 영역 그림자 설정
    self.addShadowToCommentDiv(view: commentDiv)

    // 댓글 텍스트 영역의 border 옵션 설정
    commentText.layer.borderWidth = 1.0
    commentText.layer.borderColor = borderColor.cgColor

    // 키보드 프레임 변경 노티피케이션 등록
    NotificationCenter.default.addObserver(self, selector: #selector(updateKeyboardFrame(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
            
    commentText.delegate = self // 텍스트 필드 델리게이트 설정

}

@objc func updateKeyboardFrame(_ notification: NSNotification) {
    if let userInfo = notification.userInfo,
       let keyboardFrameEnd = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
       let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
       let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber {

        // Safe Area Insets 가져오기
        let bottomSafeAreaInset = view.safeAreaInsets.bottom

        // 키보드 높이에서 Safe Area를 고려한 높이 계산
        let adjustedKeyboardHeight = keyboardFrameEnd.height - bottomSafeAreaInset

        // 키보드 애니메이션의 곡선 및 지속 시간 가져오기
        let animationCurveRaw = animationCurveRawNSN.uintValue
        let animationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw << 16)

        // commentDiv의 위치를 키보드 높이에 맞춰 애니메이션으로 변경
        UIView.animate(withDuration: animationDuration,
                       delay: 0,
                       options: animationOptions,
                       animations: {
                           if keyboardFrameEnd.origin.y >= UIScreen.main.bounds.height {
                               // 키보드가 내려간 상태면 원래 위치로 복원
                               self.commentDiv.transform = .identity
                           } else {
                               // 키보드가 나타난 상태면 Safe Area를 고려하여 올리기
                               self.commentDiv.transform = CGAffineTransform(translationX: 0, y: -adjustedKeyboardHeight)
                           }
                       }, completion: nil)
    }
}

먼저 Notification 등록은, 키보드 프레임이 변결될 때 발생하는 노티피케이션으로, 키보드의 크기나 위치가 변경될 때(ex: 키보드가 올라오거나 내려갈 때, 혹은 회전할 때 등)에 해당 이벤트를 감지할 수 있다. UIResponder.keyboardWillChangeFrameNotification 외에도 UIResponder.keyboardWillShowNotification과 UIResponder.keyboardWillHideNotification을 사용하는 방법도 있지만, 이렇게 해보니 가상키보드의 영역이 입력 도중 변화할 때(ex. 한국어 -> 일본어로 언어 전환을 하면 일본어 키보드가 한자변환 추천 영역 때문에 한국어나 영어보다 사이즈가 크다. 또한 이모지 입력시에도 키보드 크기가 변화한다)를 감지하지 못하는 문제가 있었다.

가상키보드 영역의 높이를 감지해서 IBOutlet으로 연결한 UIView(또는 UITextField)의 위치를 updateKeyboardFrame 이라는 메서드에서 변경해준다. 이때, 단순히 키보드 높이만큼 translate하는 것이 아니라, SafeAreaInset을 고려해서 높이를 조정한다. 그냥 위로 올려버리면 텍스트 입력 필드 아래에 빈 공간이 생기게 된다. safearea에 대해 조금 더 공부하고 별도의 포스팅으로 올려야겠다.

 

4. 입력 완료 이후 가상 키보드 내리기

이제 유저가 텍스트 입력을 완료하면, 가상 키보드를 적절한 방식으로 없애 주어야 하는데, 이 부분이 잘 처리되지 못한 앱을 종종 본 적이 있다. 나는 우선 유저가 UITextField 외의 다른 영역을 터치하면 키보드가 사라지는 방식을 사용했다.

// 화면의 빈 공간을 터치하면 호출되는 메서드
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    // 현재 화면에서 키보드를 숨김
    self.commentText.endEditing(true)
}

이 외에도 아래로 스와이프를 통해 키보드를 내리는 카카오톡, 리턴(엔터)키 입력으로 텍스트를 전송하며 키보드를 사라지게 하는 방식 등 다양한 방식들이 사용되고 있다. 스와이프로 키보드를 내리는 방식은 구현에 도전해봐야겠다.

 

728x90

'IBOutlet'이란?

IBOutlet(Interface Builder Outlet)은 인터페이스 빌더에서 생성된 UI 요소와 Swift 코드 간의 연결을 나타내는 키워드이다. 스토리보드에서 생성된 UI 요소(ex: 버튼, 레이블, 이미지뷰 등)를 코드에서 참조하고 조작하기 위해 사용된다.

기본적으로, 스토리보드에서 UI 요소를 선택한 후, 해당 요소를 Swift 코드의 클래스 파일에 드래그하면 자동으로 '@IBOutlet'으로 선언된 변수로 연결된다. 이렇게 연결된 변수는 코드에서 UI 요소를 직접 조작하거나 변경할 수 있는 참조가 된다.

@IBOutlet weak var myLabel: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
    myLabel.text = "Hello, World!"
}

'IBOutlet'은 변수 앞에 '@' 기호를 붙여서 선언한다. 보통 'weak' 참조로 선언되는데, 이는 메모리 관리 측면에서 순환 참조(circular reference)를 방지하기 위함이다. UI 요소는 스토리보드에서 설정되기 때문에, 'nil' 상태일 수 있으며, 따라서 옵셔널로 선언하는 경우가 많다.

 

'IBAction'이란?

IBAction(Interface Builder Acton)은 사용자 인터페이스에서 발생하는 이벤트(ex: 버튼 클릭, 슬라이더 값 변경 등)에 대한 액션을 처리하기 위해 사용되는 메서드를 나타낸다. 즉, 사용자의 인터랙션에 반응하는 함수와 인터페이스 빌더의 요소를 연결하는 방법이다.

스토리보드에서 버튼 등 이벤트를 발생시키는 UI 요소를 선택한 후, 이를 Swift 코드로 드래그하면 자동으로 '@IBAction'이 붙은 메서드로 연결된다. 이 메서드는 해당 이벤트가 발생할 때 호출된다. 아래는 스토리보드에서 버튼을 추가하고, 이 버튼이 클릭될 때 실행될 액션을 정의하는 예시이다. 버튼을 클릭했을 때, 레이블의 텍스트를 변경하는 동작을 수행한다.

@IBAction func myButtonTapped(_ sender: UIButton) {
    myLabel.text = "Button Clicked!"
}

 

'IBOutlet'과 'IBAction'의 상호작용

보통 IBOutlet과 IBAction은 함께 사용된다. 예를 들어, UI에서 버튼을 클릭하면 레이블의 텍스트를 변경하는 상황에서 레이블을 IBOutlet으로 연결하고, 버튼의 클릭 액션을 IBAction으로 처리한다.

@IBOutlet weak var myLabel: UILabel!

@IBAction func buttonTapped(_ sender: UIButton) {
    myLabel.text = "The button was tapped!"
}

이렇게 하면 UI 요소와 이벤트 핸들링 로직을 코드에서 분리하면서도, UI요소의 상태를 코드에서 제어할 수 있다.

728x90

swift를 배워보자
swift를 배워보자

이전글: 2024.08.03 - [개발/swift] - swift를 이용한 iOS 앱 개발 입문 1: 각 파일들의 역할

swift를 이용한 iOS 앱 개발 입문 2: 앱(App), 씬(Scene), 생명주기(Lifecycle)

늘상 웹개발만 하다가, 앱개발의 세계에 처음 발을 담궈보니, 앱의 생명주기라는 것에 대해 알 필요가 있어서 정리해본다. 이전 글에서 앱의 생명주기를 'AppDelegate.swift'라는 파일에서 관리한다고 적었는데, 조금 더 깊이 들어가보자.

앱의 생명주기(App Lifecycle)은 iOS앱이 실행되는 동안 다양한 상태 전환을 설명하는 중요한 개념이다. 앱의 생명주기를 이해하면 앱의 시작, 중지, 다시 시작, 종료 시 적절한 작업을 수행할 수 있다. 다음은 iOS앱의 생명주기와 각 상태에 대한 자세한 설명이다.

 

1. 앱의 상태

앱은 다음과 같은 상태를 가질 수 있다:

  • Not Running: 앱이 실행되지 않음
  • Inactive: 앱이 포어그라운드에 있지만 이벤트를 처리하지 않음
  • Active: 앱이 포어그라운드에 있으며 이벤트를 처리하고 있음
  • Background: 앱이 백그라운드에 있지만 여전히 코드 실행이 가능
  • Suspended: 앱이 백그라운드에 있으며 코드 실행이 중단됨. 메모리에 유지됨

 

2. 씬(Scene)은 뭔데?

유사하게 'SceneDelegate.swift'라는 파일에는 씬의 생명주기를 관리하는데, 그럼 씬은 도대체 뭘까 하고 찾아보니... 씬은 iOS 13 이후 도입된 개념으로, 하나의 앱 내에서 여러 개의 윈도우를 관리할 수 있게 한다. 씬은 사용자가 동시에 여러 개의 UI환경을 다룰 수 있도록 지원한다. 씬의 주요 특징으로는,

  • 멀티태스킹 지원: 씬을 사용하면 사용자가 하나의 앱에서 여러 개의 독립적인 UI 환경을 동시에 사용할 수 있다. 예를 들어, iPad에서 멀티윈도우 모드를 사용할 수 있다.
  • 독립적인 생명 주기: 각 씬은 독립적인 생명 주기를 가지고 있으며, 'SceneDelegate'를 통해 관리된다. 각 씬의 생성, 활성화, 비활성화, 종료 등의 이벤트를 개별적으로 처리한다.
  • 윈도우 관리: 씬은 하나 이상의 윈도우를 포함할 수 있으며, 각 윈도우는 자체적인 뷰 컨트롤러 계층을 갖고 있다.

 

3. 생명주기 이벤트를 처리하는 메서드

위에서 알아본 생명주기의 변화 이벤트를 받아주는 메서드가 존재하여, 'AppDelegate.swift'파일과 'SceneDelegate.swift'파일에서 이러한 이벤트를 핸들링 해 주어야 할 것이다. 아래는 앱과 씬의 생명주기에 관련된 메서드들을 소개한다.

AppDelegate 메서드

  1. application(_:didFinishLaunchingWithOptions:) - 앱이 시작될 때 호출. 앱의 초기 설정을 여기서 진행
  2. applicationDidBecomeActive(_:) - 앱이 활성 상태로 전환될 때 호출. 일반적으로 앱이 포어그라운드로 들어올 때나 일시 정지 상태에서 돌아올 때 호출
  3. applicationWillResignActive(_:) - 앱이 비활성 상태로 전환되기 전에 호출. 전화나 메시지 등으로 인해 일시 중단될 때 호출
  4. applicationDidEnterBackground(_:) - 앱이 백그라운드로 전환될 때 호출. 상태를 저장하거나 리소스를 해제하는 등의 작업을 여기서 수행
  5. applicationWillEnterForeground(_:) - 앱이 백그라운드에서 포어그라운드로 전환될 때 호출
  6. applicationWillTerminate(_:) - 앱이 종료될 때 호출. 상태를 저장하고 정리 작업을 수행

SceneDelegate 메서드

  1. scene(_:willConnectTo:options:) - 새로운 씬이 생성될 때 호출. 씬의 초기 설정을 여기서 진행
  2. sceneDidDisconnect(_:) - 씬이 연결 해제될 때 호출. 씬이 더 이상 사용되지 않음을 의미
  3. sceneDidBecomeActive(_:) - 씬이 활성 상태로 전환될 때 호출
  4. sceneWillResignActive(_:) - 씬이 비활성 상태로 전환되기 전에 호출
  5. sceneWillEnterForeground(_:) - 씬이 백그라운드에서 포어그라운드로 전환될 때 호출
  6. sceneDidEnterBackground(_:) - 씬이 백그라운드로 전환될 때 호출

이렇게 보니까 앱과 씬이 개념적으로는 좀 구분이 되는 듯 하기도 하고...  역시 가장 중요한 것은 직접 부딪히며 코드를 짜보는게 이해하기에 가장 쉽고 빠른 방법인 듯 하다. 사이드 프로젝트 앱의 스플래시 화면을 만들어가며 공부해보자.

728x90

swift를 배워보자
swift를 배워보자

swift를 이용한 iOS 앱 개발 입문 1: 각 파일들의 역할

Xcode로 프로젝트를 생성하고 난 뒤, 친절한 튜토리얼을 찾기 어려워서 스스로 정리해 보기로 결심했다.

 

프로젝트를 생성하면, AppDelegate.swift, SceneDelegate.swift, LaunchScreen.storyboard, ViewController.swift 와 같은 파일들이 같이 생성된다. 각 파일들의 역할을 간략하게 짚고 넘어가자.

 

1. AppDelegate.swift

'AppDelegate.swift' 파일은 앱의 생명 주기를 관리하는 핵심 파일이다. 앱의 시작, 상태 전환, 종료 등의 주요 이벤트를 처리한다.

주요 역할

1. 앱이 시작될 때 초기 설정

2. 푸시 알림 처리

3. 백그라운드 및 포어그라운드 상태 전환 처리

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    // 앱 시작 시 호출
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 초기화 코드
        return true
    }
    
    // 앱이 백그라운드로 전환될 때 호출
    func applicationDidEnterBackground(_ application: UIApplication) {
        // 백그라운드 전환 시 작업
    }
    
    // 앱이 포어그라운드로 돌아올 때 호출
    func applicationWillEnterForeground(_ application: UIApplication) {
        // 포어그라운드 복귀 시 작업
    }
}

 

2. SceneDelegate.swift

'SceneDelegate.swift' 파일은 iOS 13 이후 도입된 멀티 윈도우 및 멀티 씬 지원을 위한 파일로 각 씬의 생명 주기를 관리한다.

주요 역할

1. 씬 생성 및 파괴 처리

2. 씬의 활성화 및 비활성화 상태 관리

3. UI 상태 복원 및 유지

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    // 씬이 활성화될 때 호출
    func sceneDidBecomeActive(_ scene: UIScene) {
        // 씬이 활성화될 때 작업
    }
    
    // 씬이 비활성화될 때 호출
    func sceneWillResignActive(_ scene: UIScene) {
        // 씬이 비활성화될 때 작업
    }
}

 

3. LaunchScreen.storyboard

'LaunchScreen.storyboard' 파일은 앱이 실행될 때 처음으로 보여지는 화면을 정의한다. 이 화면은 앱이 실제로 로드되기 전에 사용자에게 표시되며, 스플래시 화면으로 사용됨.

주요 역할

1. 앱이 로드될 때 사용자에게 보여지는 초기 화면 디자인

2. 초기 로딩 시간을 감추기 위해 간단한 이미지나 로고 표시

 

4. ViewController.swift

'ViewController.swift' 파일은 기본적으로 생성되는 뷰 컨트롤러로, 앱의 첫 번째 화면을 관리한다. 뷰의 로드 및 사용자 인터페이스 이벤트를 처리.

주요 역할

1. 뷰 로드 시 초기화 작업

2. 사용자 상호작용 처리 및 UI 업데이트 관리

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 뷰가 로드될 때 초기화 작업
    }
}

 

간단히 정리하면, .storyboard 파일은 UI를 그리는 html+css 같은 역할을, viewcontroller.swift 파일은 액션을 정의하는 javascript와 유사하다고 이해하였음.

728x90

+ Recent posts