티스토리 뷰
첫 macOS 앱 출시: 개발 회고 및 기술 공유
iOS 앱을 macOS로 확장하면서 "SwiftUI니까 금방 되겠지"라고 생각했다. 결론부터 말하면, 생각보다 신경 쓸 부분이 많았다. 같은 Swift, 같은 SwiftUI인데도 플랫폼마다 다른 부분들이 꽤 있었고, App Store 제출 과정에서도 예상치 못한 요구사항들을 마주했다.
이 글에서는 첫 macOS 앱을 출시하면서 겪은 경험들을 정리해본다.
UI/UX 차이점
System Color가 공유되지 않는다
iOS에서 자주 사용하던 UIColor.systemBackground, UIColor.label 같은 시스템 컬러들이 macOS에서는 그대로 사용할 수 없다. macOS는 NSColor를 사용하며, 일부 시스템 컬러의 이름이나 동작 방식이 다르다.
SwiftUI에서 Color.primary, Color.secondary 같은 semantic color는 양쪽에서 동작하지만, 플랫폼별로 세밀한 색상 조정이 필요하다면 #if os(macOS) 분기 처리가 필요하다.
#if os(macOS)
let backgroundColor = Color(NSColor.windowBackgroundColor)
#else
let backgroundColor = Color(UIColor.systemBackground)
#endif
.navigationBarTitleDisplayMode(.inline)이 없다
iOS에서 네비게이션 타이틀 스타일을 조정할 때 흔히 사용하는 .navigationBarTitleDisplayMode(.inline)이 macOS에는 존재하지 않는다. macOS의 윈도우 타이틀바는 iOS의 네비게이션 바와 개념 자체가 다르기 때문이다.
macOS에서는 .navigationTitle()만 사용하거나, 필요하다면 toolbar를 활용해 커스텀하게 구성해야 한다.
NavigationSplitView 활용
macOS 앱에서는 사이드바 기반의 레이아웃이 일반적이다. NavigationSplitView를 활용하면 macOS다운 2단 또는 3단 레이아웃을 쉽게 구현할 수 있다.
NavigationSplitView {
// Sidebar
List(items, selection: $selectedItem) { item in
Text(item.name)
}
} detail: {
// Detail View
if let selectedItem {
DetailView(item: selectedItem)
} else {
Text("항목을 선택하세요")
}
}
iOS에서는 NavigationStack으로 push/pop 방식을 주로 사용했다면, macOS에서는 NavigationSplitView로 마스터-디테일 패턴을 적용하는 것이 더 자연스럽다.
플랫폼별 API 차이
UIPasteboard 대신 NSPasteboard
클립보드 기능을 구현할 때 iOS에서는 UIPasteboard.general을 사용하지만, macOS에서는 NSPasteboard.general을 사용해야 한다.
// iOS
UIPasteboard.general.string = "복사할 텍스트"
// macOS
NSPasteboard.general.clearContents()
NSPasteboard.general.setString("복사할 텍스트", forType: .string)
macOS의 NSPasteboard는 clearContents()를 먼저 호출해야 하는 점도 다르다.
UIApplicationDelegate 대신 NSApplicationDelegate
앱 생명주기 관리를 위해 iOS에서는 UIApplicationDelegate를 사용하지만, macOS에서는 NSApplicationDelegate를 사용한다.
// iOS
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
}
// macOS
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
// 초기화 로직
}
}
SwiftUI의 @main App 구조체를 사용한다면 @NSApplicationDelegateAdaptor로 연결할 수 있다.
App Store 제출 시 필수 사항
macOS 전용 아이콘 필수
iOS 아이콘만 있으면 될 줄 알았는데, macOS 전용 아이콘을 별도로 추가하지 않으면 아카이브 업로드 자체가 거부된다. macOS 아이콘은 1024x1024 크기에 둥근 사각형이 아닌 원형 마스크가 적용된 형태다.
Asset Catalog에서 AppIcon에 macOS용 슬롯을 채워넣어야 한다.
LSApplicationCategoryType 설정 필수
Info.plist에 LSApplicationCategoryType을 반드시 설정해야 한다. 설정하지 않으면 App Store Connect 업로드가 실패한다.
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
카테고리 값은 Apple 문서에서 확인할 수 있다.
App Sandbox 활성화 필수
macOS 앱은 App Sandbox가 반드시 활성화되어 있어야 App Store에 제출할 수 있다. Xcode의 Signing & Capabilities에서 App Sandbox를 추가하고, 필요한 권한만 체크하면 된다.
네트워크 통신이 필요하다면 Outgoing Connections, 파일 접근이 필요하다면 해당 권한을 활성화해야 한다.
빌드 및 배포 과정
아카이브와 업로드를 각각 진행
iOS와 macOS 빌드는 별도로 아카이브하고 각각 업로드해야 한다. Universal 앱이라 하더라도 하나의 아카이브로 두 플랫폼을 동시에 제출하는 것은 불가능하다.
Xcode에서 빌드 타겟을 macOS로 변경한 후 Product > Archive를 진행하면 된다.
심사도 각각 진행
App Store 심사 역시 iOS와 macOS가 별개로 진행된다. 한쪽이 승인되어도 다른 쪽은 여전히 심사 중일 수 있고, 리젝 사유도 플랫폼마다 다를 수 있다.
TestFlight Universal 앱 설치 시 주의점
TestFlight에 Universal 앱으로 모든 플랫폼 빌드를 올렸을 때 예상치 못한 동작이 있었다. macOS에서 TestFlight로 앱을 설치하면 네이티브 macOS 빌드가 아닌 iOS 빌드(Designed for iPad)가 우선 설치된다.
Apple이 iOS 앱을 Mac에서 실행 가능하다고 판단하여 iOS 빌드에 우선순위를 부여하기 때문이다. 테스터에게 네이티브 macOS 버전을 테스트하게 하려면 별도 안내가 필요하다.
서드파티 SDK 호환성
Google AdMob은 macOS 미지원
수익화를 위해 Google AdMob을 사용하고 있었는데, AdMob SDK는 iOS 전용이라 macOS에서는 사용할 수 없다. macOS 앱에서 광고를 넣으려면 다른 광고 네트워크를 찾거나, 광고 없이 유료 앱 또는 인앱 결제 모델을 고려해야 한다.
마무리
iOS 개발 경험이 있다면 macOS 앱 개발 자체는 크게 어렵지 않다. SwiftUI 덕분에 많은 코드를 공유할 수 있고, 플랫폼별 분기 처리만 잘 해주면 된다.
다만 App Store 제출 과정에서 macOS 특유의 요구사항들(아이콘, 카테고리, 샌드박스)을 미리 알아두지 않으면 업로드 단계에서 막힐 수 있다. 이 글이 첫 macOS 앱을 준비하는 분들에게 도움이 되었으면 한다.
macOS
SwiftUI
Swift
iOS
Apple
App Store
Xcode
macOS 개발
macOS 앱
NavigationSplitView
NSPasteboard
App Sandbox
TestFlight
Universal App
iOS 개발자
앱 출시
앱스토어 심사
크로스 플랫폼
Apple Developer
'macOS' 카테고리의 다른 글
| macOS SwiftUI 맥앱 바로가기 아이콘 빼내기 (add mac app icon to dock) (1) | 2024.06.01 |
|---|---|
| macOS SwiftUI 폴더 선택, 파일 이동 (move file) (2) | 2024.05.01 |
- Total
- Today
- Yesterday
- macos
- objective-c
- swiftUI
- 심사
- Xcode
- 인디케이터
- SWIFT
- Apple
- indicator
- Reject
- 엑스코드
- 스위프트
- 프로그레스
- 리젝
- 테이블뷰
- Authorization
- localizable
- TabBar
- ios
- 현지화
- picker
- TabView
- permission
- AppStore
- localizing
- Language
- connect
- 아이오에스
- 로컬라이징
- 다국어
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
