2024. 2. 1. 11:09ㆍ🍏/Swift
swift packages에 대해서 정리하고 알아보던 도중 좋은 글이 있어서, 읽어보면서 정리해보려고 쓰는 글입니다.
Module
Swift의 코드 구성과 접근 제어 개념은 모듈을 기반으로 합니다. 이 뜻은 하나의 타겟 내에서 모든 아래와 같은 모듈들을 internal 접근 수준으로 모든 코드에 접근이 가능하다는 뜻입니다.
- UI Module: 사용자 인터페이스와 관련된 코드를 포함합니다. ViewController, View, 그리고 각종 UI 구성 요소 관련 코드가 여기에 속합니다.
- Networking Module: 네트워크 통신에 관련된 코드를 포함합니다. API 호출, 데이터 다운로드 및 업로드, 네트워크 오류 처리 등이 이 모듈에 포함될 수 있습니다.
- Model Module: 앱에서 사용되는 데이터 모델과 관련된 코드를 포함합니다. 데이터 구조, 데이터 유효성 검사, 데이터 변환 등이 여기에 속합니다.
- Service Module: 외부 서비스와의 상호 작용에 사용되는 코드를 포함합니다. 데이터베이스 액세스, 로컬 저장소 관리, 외부 API 호출 등이 이 모듈에 속할 수 있습니다.
- Utility Module: 일반적인 유틸리티 및 도우미 함수가 포함될 수 있는 모듈입니다. 예를 들어, 날짜 변환, 문자열 처리, 파일 관리 등의 유틸리티 함수가 이곳에 속합니다.
- Constants Module: 상수 및 열거형 값들을 정의하는 모듈입니다. 앱 전반에서 사용되는 상수, 테마 관련 값, 열거형 타입 등이 이곳에 정의될 수 있습니다. 예를 들어, 앱의 테마 색상, 글꼴 크기, 앱 내에서 공통적으로 사용되는 문자열 상수 등을 정의할 수 있습니다.
프로그램은 모든 코드를 단일 모듈에 포함할 수도 있고 다른 모듈을 종속성으로 임포트할 수도 있습니다. macOS의 Darwin이나 Linux의 Glibc와 같은 소수의 시스템 제공 모듈을 제외하고 대부분의 종속성을 사용하려면 코드를 다운로드하여 빌드해야 합니다.
특정 문제를 해결하는 코드에 별도의 모듈을 사용하면 해당 코드를 다른 상황에서 재사용할 수 있습니다. 예를 들어, 네트워크 요청 기능을 제공하는 모듈을 사진 공유 앱과 날씨 앱에서 공유할 수 있습니다. 모듈을 사용하면 동일한 기능을 직접 다시 구현하지 않고 다른 개발자의 코드를 기반으로 구축할 수 있습니다.
- UI Module:
- ViewController: 사용자 인터페이스를 관리하고 사용자 입력에 응답하는 클래스입니다.
- View: 화면에 보이는 요소를 표시하고 사용자 상호 작용을 처리하는 객체입니다.
- Storyboard: 사용자 인터페이스의 시각적 구조를 정의하는 파일입니다.
- Networking Module:
- URLSession: 네트워크 요청을 생성하고 수행하는 객체입니다.
- Codable Protocol: 네트워크로부터 수신된 데이터를 모델 객체로 디코딩하고, 모델 객체를 인코딩하여 서버로 전송할 수 있도록 도와주는 프로토콜입니다.
- Alamofire: 네트워킹 작업을 단순화하고 추상화하는 Swift 기반의 HTTP 클라이언트 라이브러리입니다.
- Model Module:
- Codable Structs: 데이터 모델을 나타내는 구조체들로, JSON 데이터를 파싱하고 저장하기 위해 Codable 프로토콜을 준수합니다.
- CoreData Entities: 영구 저장소에 저장되는 데이터를 모델링하는 CoreData 엔터티들입니다.
- Service Module:
- Firebase: 클라우드 기반의 서비스로, 데이터베이스, 인증, 클라우드 메시징 등 다양한 서비스를 제공합니다.
- CoreLocation: 사용자의 현재 위치를 가져오고 위치 기반 서비스를 제공하는 프레임워크입니다.
- Utility Module:
- Date Utilities: 날짜 및 시간을 다루는 유틸리티 함수들입니다.
- String Utilities: 문자열 처리를 위한 유틸리티 함수들입니다.
- Constants Module:
- Theme Constants: 앱의 테마 관련 상수들로, 색상, 글꼴 크기, 간격 등을 정의합니다.
- API Endpoint Constants: 외부 API의 엔드포인트 URL 등을 정의하는 상수들입니다.
Bundle
Apple의 번들은 앱, 프레임워크, 플러그인 등과 같은 여러 특정 유형의 콘텐츠를 나타내며, 포함된 리소스를 잘 정의된 하위 디렉토리로 구성합니다. 번들의 구조는 플랫폼과 번들의 유형에 따라 다를 수 있으며, 번들 개체를 사용하여 번들의 구조를 몰라도 리소스에 액세스할 수 있습니다.
번들 개체를 사용하는 일반적인 패턴은 다음과 같습니다:
- 원하는 번들 디렉터리에 대한 번들 객체를 만듭니다.
- 번들 객체의 메서드를 사용하여 필요한 리소스를 찾거나 로드합니다.
- 다른 시스템 API를 사용하여 리소스와 상호 작용합니다.
번들을 사용하는 또 다른 일반적인 패턴은 다음과 같습니다:
- 이미지를 로드할 때 자산 카탈로그에 이미지를 저장하고 UIImage 또는 NSImage의 init(named:) 메서드를 사용하여 이미지를 로드합니다.
- 문자열 리소스의 경우 NSLocalizedString을 사용하여 개별 문자열을 로드합니다.
번들은 앱의 핵심적인 구성 요소 중 하나이며, 번들을 효과적으로 활용하여 앱의 리소스와 설정을 관리하고 다양한 플랫폼에서의 호환성을 유지하는 것이 중요합니다. 번들을 사용하여 앱의 구조를 모듈화하고 유지보수 가능하게 만들어줌으로써 앱의 성능과 사용자 경험을 향상시킬 수 있습니다.
이미지 리소스 로드:
- 앱 번들 내의 이미지 파일을 로드하여 화면에 표시하는 경우:
if let imagePath = Bundle.main.path(forResource: "exampleImage", ofType: "png") {
let image = UIImage(contentsOfFile: imagePath)
imageView.image = image
}
문자열 리소스 로드:
- 앱의 로컬라이즈된 문자열 리소스를 번들에서 로드하는 경우:
let localizedString = NSLocalizedString("welcome_message", tableName: "Localizable", bundle: Bundle.main, value: "Welcome", comment: "Welcome message") label.text = localizedString
플리스토어 리소스 로드:
- 앱 번들 외부에 있는 리소스를 로드하는 경우 (예: 플리스토어에 다운로드한 파일):
if let resourceURL = Bundle(path: "/path/to/external/bundle.bundle")?.url(forResource: "exampleFile", withExtension: "txt") { // Load resource from external bundle }
번들 정보 조회:
- 번들의 정보를 조회하여 앱 버전 및 빌드 번호를 표시하는 경우:
if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String, let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { versionLabel.text = "Version \(appVersion) (\(buildNumber))" }
- 새로운 번들 프로젝트 생성:
- Xcode를 열고 "File" 메뉴에서 "New"를 선택한 후 "Project"를 클릭합니다.
- 나타나는 창에서 "macOS" 또는 "iOS" 카테고리에서 "Framework & Library"를 선택합니다.
- "Bundle" 템플릿을 선택한 후 "Next"를 클릭합니다.
- 프로젝트의 이름과 위치를 지정한 후 "Create"를 클릭하여 번들 프로젝트를 생성합니다.
- 번들 구성:
- 생성된 번들 프로젝트에서 번들에 포함할 리소스 파일을 추가합니다. 이미지, 문자열 파일, 사운드 파일 등을 포함할 수 있습니다.
- 번들 프로젝트의 타겟 설정에서 "Build Phases"로 이동하여 번들에 포함할 파일을 추가하거나 제거합니다.
- 필요에 따라 번들의 속성을 설정합니다. 예를 들어, Info.plist 파일을 편집하여 번들의 정보를 지정할 수 있습니다.
- 번들 빌드:
- Xcode에서 "Product" 메뉴에서 "Build"를 선택하여 번들을 빌드합니다.
- 빌드가 완료되면 번들이 생성되고 번들 내의 리소스 파일이 포함됩니다.
- 번들 사용:
- 번들을 사용하려면 해당 번들을 앱 프로젝트에 추가해야 합니다. 이를 위해 앱 프로젝트에서 번들을 import하거나 번들을 포함하는 앱의 타겟에 번들을 추가해야 합니다.
- 앱에서 번들에 포함된 리소스를 로드하거나 사용할 수 있습니다.
번들을 만드는 방법은 프로젝트 유형 및 목적에 따라 다를 수 있습니다. 위의 단계는 일반적인 방법을 보여주며, 특정 프로젝트에 따라 추가적인 설정이 필요할 수 있습니다.
Libraries
Static Libraries
Static library를 사용하면 Static linker가 작성한 소스 코드들과 라이브러리 코드들을 연결하고 최종적으로 Source files 와 static library가 결합된 Executable file이 생성 되어 프로그램에 포함 됩니다.
Static library를 사용한다면
해당 라이브러리 코드들은 모두 프로그램 내부에 포함.
Static library의 특징
- 라이브러리 코드가 실행 파일 내부에 포함되므로
실행 파일의 크기가 커집니다.
- Static Library가 업데이트 된다면, 다시 link 작업을 해야 한다. 다시 컴파일 하는 과정이 생길 수 있습니다
- image, asset, nib, string file과 같은 파일들을 포함할 수 없습니다.
Dynamic Library (*.dylib)
Dynamic Library는 Static Library와는 다르게 실행 파일 내부에 라이브러리 코드들이 포함되지 않습니다. 대신 static linker는 library에 대한 참조를 실행 파일 내부에 저장시키며, 프로그램을 로드하거나 실행할 때 동적으로 메모리 내에 상주하고 있는 라이브러리 파일들과 연결됩니다.
System iOS와 macOS 라이브러리는 모두 dynamic library. system library는 모든 앱에서 사용하게 되는데, 만약 system library가 static 이면 모든 앱에 중복된 코드들이 포함되면 모든 앱들의 용량이 지금보다 훨씬 커질 것입니다.
Dynamic library의 특징
- 실행 파일 내부엔 dynamic library에 대한 참조만 저장하고 있으므로, 만약 이후에 dynamic library가 업데이트 된다고 해도 다시 컴파일 할 필요가 없이 업데이트 된 사항들을 모두 적용할 수 있습니다.
In spite of this Apple's documents clearly says:
Dynamic libraries outside of a framework bundle, which typically have the file extension .dylib, are not supported on iOS, watchOS, or tvOS, except for the system Swift libraries provided by Xcode.
Frameworks
프레임워크는 dynamic library, header files, and resources, such as storyboards, image files, and localized strings를 단일 패키지로 캡슐화하는 계층적 디렉터리입니다. 프레임워크를 사용하는 앱은 앱의 번들에 프레임워크를 포함해야 합니다.
프레임워크는 정적 및 동적 공유 라이브러리와 동일한 문제 해결을 위한 것입니다.
- 라이브러리와는 달리 프레임워크에는 이미지, 에셋, 문서, 문자열 파일과 같은 리소스가 포함될 수 있습니다.
- 프레임워크 읽기 전용 리소스의 복사본이 하나만 로드되어 메모리에 상주하게 된다. 따라서 많은 iOS App과 extension이 하나의 프레임워크를 공유할 수 있어, 총 메모리 사용량이 줄어들게 됩니다.
제어 역전의 관점에서 Library와 Framework의 차이점
- Library에 존재하는 모든 함수들과 코드들은 사용하는 클라이언트 쪽에서 호출해야 한다. 즉, Client가 Library의 코드를 호출하고 제어한다는 의미입니다.
- 그러나 Framework는 Library와 다르게, 우리가 제어하는 것이 아니라 Framework에 존재하는 인터페이스, 클래스들을 상속 혹은 구현함으로써 원하는 행동을 주입할 수 있다. 그 이후, Framework가 우리가 주입한 행동들을 호출합니다.
- 즉 Framework가 우리가 작성한 코드들을 호출하고, 흐름을 제어한다는 의미입니다.
- Framework에서 호출하는 이벤트들을 interface 형식으로 정의하거나 (Button의 클릭 이벤트, delegate 등등...) 아니면 상속 가능한 class 형태로 제공합니다. 또한 Notification 과 같은 패턴을 사용해서 우리가 원하는 행동을 주입하기도 합니다.
- 예를 들어 Keyboard가 올라오는 이벤트를 Notification Center에서 전달해줍니다.우리가 만약 Keyboard가 올라올 때 특정한 행동을 하고 싶으면 그런 코드를 Notification center에 넘겨주면 된다. 그러면 Notification center가 키보드가 올라올 때마다 우리가 주입한 행동을 실행하게 된다.
이렇게 Framework 는 제어 역전을 제공함으로써, 객체의 라이프 사이클과 흐름을 모두 Framework가 결정하게 된다. 단지 우리는 Framework에 원하는 행동 (비즈니스 로직)만 주입하면 되는 것입니다.
Package
Package.swift 파일에선, package가 어떤 형태로 배포될지, 또 어떤 외부 라이브러리에 의존할지, 그리고 어떤 타겟들을 배포에 포함할지, 그리고 어떤 플랫폼에서 사용 가능한 패키지인지도 결정할 수 있습니다.
Swift 패키지는 3부분으로 구성됩니다: 의존성(dependencies), 타겟(targets), 그리고 제품(products):
- dependencies: 패키지 내에서 사용하는 다른 Swift 패키지입니다. 패키지 파일에서 각 의존성은 소스 위치와 버전으로 지정됩니다.
- targets: Apple 문서에 따르면, 타겟은 패키지의 기본 빌딩 블록입니다. 라이브러리나 실행 가능한 파일을 제품으로 가질 수 있습니다. Xcode 12 이전에는 Swift 패키지가 Swift, Objective-C/C++, 또는 C/C++ 파일만 포함할 수 있었습니다. Xcode 12는 새로운 tools-version:5.3을 제공하며, 새로운 패키지 매니페스트 API를 도입합니다. 이제 Swift 패키지는 이미지, 스토리보드, JSON 파일, 셸 스크립트 등 다른 리소스 유형을 포함할 수 있습니다. 이러한 리소스를 현지화할 수도 있습니다. 이것은 큰 개선이며, 이를 가능하게 한 모든 기여자에게 감사합니다. 유용한 세부 사항과 구현 세부 사항은 WWDC 2020 비디오 Swift packages: Resources and localization 및 문서 Localizing Package Resources에서 찾을 수 있습니다.
- products: 패키지의 출력물로, 다른 패키지에게 보이는 라이브러리나 실행 파일을 생성합니다. Swift Package가 최종적으로 어떤 형식으로 배포될 것인지를 결정한다. 위에선 library 형식으로 배포된다고 적혀있는데, dynamic 인지 static인지도 결정할 수 있고, library말고 plugin, executable 형태로도 배포가 가능합니다.
Swift 패키지의 장단점은 다음과 같습니다.
장점:
- Xcode가 의존성을 관리합니다.
- Xcode가 버전을 관리합니다.
- 바이너리 호환성이 필요 없으며, 패키지는 같은 빌드 작업에서 여러 플랫폼에 대해 컴파일될 수 있습니다. 각 플랫폼마다 별도의 프레임워크 타겟을 생성할 필요가 없습니다.
- 소스 형태로 배포되므로 코드를 검사하고 디버깅하는 동안 그 안으로 들어갈 수 있습니다.
- Xcode 12부터 Swift 패키지는 이미지, 스토리보드, XCFrameworks 및 기타 파일 유형을 포함할 수 있습니다.
단점:
- (Xcode 12 이전) Swift 패키지는 소스 코드를 포함하지만 에셋과 리소스를 지원하지 않습니다.
- (Xcode 12 이전) Swift 패키지는 다른 Swift 패키지에만 의존할 수 있으며, 바이너리 의존성은 지원되지 않습니다.
- 소스 형태로 배포되므로 소스 코드를 공유하고 싶지 않은 프레임워크 제공자에게는 적합하지 않습니다.
iOS 애플리케이션의 아키텍처 선택 시 정적 라이브러리, 프레임워크, Swift 패키지 간의 구체적인 프로젝트 제한 사항을 고려해야 합니다.
앱에 너무 많은 정적 라이브러리를 연결하면 앱 실행 파일의 크기가 커지고, 시작 시간이 느려지며, 메모리 사용량이 증가합니다. 프레임워크는 정적 라이브러리보다 훨씬 더 유연하지만, 프로젝트에 추가된 각 임베디드 프레임워크는 시작 시간을 증가시킵니다.
소스 파일을 배포할 수 있다면, Swift 패키지가 여러분에게 적합할 수 있습니다. Xcode는 모든 의존성, 버전 관리, 플랫폼을 처리해 줍니다.
'🍏 > Swift' 카테고리의 다른 글
[Swift] Dynamic Libraries or Static Libraries / 동적 라이브러리, 정적라이브러리 (2) | 2024.02.08 |
---|---|
[SPM] Swift Package Manager / 내 Library 만들기 (0) | 2024.02.01 |
[Framework] WebKit (0) | 2023.08.18 |
[Swift] 코테 기초 정리 / 기초적인 고차 함수 정리 / 라이브러리 함수 정리 / 스위프트 코테에서 알아두면 좋은 라이브러리 함수들 정리 (0) | 2023.05.19 |
[Swift] Some / Any (0) | 2023.04.15 |