티스토리 뷰

 

사용자의 디바이스 모델명, OS, 앱 버전 상태를 확인하기 위한 방법을 알아보자.

이런 정보는 디버깅, 사용자 지원, UI 최적화, 기능 테스트, 마케팅 및 분석등 다양한 목적으로 사용될 수 있어 요구사항에 따라 적절하게 활용하면 된다.

 

 

📖 목차

1️⃣ 클래스 정의
2️⃣ 클래스 사용 예시
3️⃣ 주의 사항

 

 


1. 클래스 정의

 

 

🟡 속성

 

필요한 데이터인 모델명, 운영체제/앱 버전을 아래와 같이 정의한다.

 

  • osVersion: 기기 운영체제 이름과 버전
  • deviceModel: 기기 모델 이름
  • appVersion: 앱의 버전

 

🟡 초기화 메서드

 

init()

: 클래스의 인스턴스를 초기화하고, osVersion, deviceModel, appVersion 속성에 값을 할당한다.

 

1️⃣ osVersion 초기화

 

let device = UIDevice.current
osVersion = "\(device.systemName) \(device.systemVersion)"

 

UIDevice.current를 사용하여 현재 기기에 대한 참조를 가져온다.
osVersion 속성은 기기의 운영체제 이름(systemName)과 버전(systemVersion)을 조합하여 설정한다.

 

 

2️⃣ deviceModel 초기화

 

var modelName = ""
let selName = "_\("deviceInfo")ForKey:"
let selector = NSSelectorFromString(selName)

if device.responds(to: selector) {
    modelName = String(describing: device.perform(selector, with: "marketing-name").takeRetainedValue())
}
deviceModel = modelName

 

 

NSSelectorFromString을 사용하여 _deviceInfoForKey:라는 셀렉터를 동적으로 생성하고, perform 메서드를 사용하여 해당 셀렉터를 호출하고 있다. 

_deviceInfoForKey:는 Apple의 공식 문서에 공개되지 않은 비공개 메서드이다.

 

이 부분이 비공개 API를 사용한 부분이다.

 

3️⃣ appVersion 초기화

 

if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
   let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String
{
    appVersion = "\(version)"
} else {
    appVersion = "Unknown"
}

 

Bundle.main.infoDictionary에서 CFBundleShortVersionString 및 CFBundleVersion 키를 사용하여 앱의 버전 정보를 가져온다. 가져온 버전 정보를 appVersion 속성에 할당하고, 만약 버전 정보를 가져오지 못하면 "Unknown"으로 설정된다.

 

 

전체 코드

 

import SwiftUI

class DeviceInfoHandler: ObservableObject {
    @Published var osVersion: String
    @Published var deviceModel: String
    @Published var appVersion: String

    init() {
        let device = UIDevice.current
        osVersion = "\(device.systemName) \(device.systemVersion)"

        var modelName = ""
        let selName = "_\("deviceInfo")ForKey:"
        let selector = NSSelectorFromString(selName)

        if device.responds(to: selector) {
            modelName = String(describing: device.perform(selector, with: "marketing-name").takeRetainedValue())
        }
        deviceModel = modelName

        if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
           let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String
        {
            appVersion = "\(version)"
        } else {
            appVersion = "Unknown"
        }
    }
}

 


2. 클래스 사용 예시

 

 

앱 초기화 시점에서 DeviceInfoHandler를 사용하여 기기 및 앱 정보를 수집하고 출력해보자.

이를 통해 앱 실행 시점에서 어떤 기기와 운영체제 버전이 실행되고 있는지 확인할 수 있다.

 

@main
struct ExampleApp: App {

    init() {
        let deviceInfoHandler = DeviceInfoHandler()
        print("OS Version: \(deviceInfoHandler.osVersion)")
        print("Device Model: \(deviceInfoHandler.deviceModel)")
        print("App Version: \(deviceInfoHandler.appVersion)")
    }
}

 

 

시뮬레이터를 실행하면 앱 실행 시점에서 아래와 같이 출력되는 걸 확인할 수 있다.

 

 

 


3. 주의 사항

 

🔴 비공개 API 사용 문제

 

 

위에서 비공개 API를 빨간색으로 강조한 이유가 있다.

Apple의 비공개 API는 공식적으로 문서화되지 않았고, Apple이 내부적으로 사용하는 API를 말한다.

 

비공개 API를 사용하는 것은 다음과 같은 이유로 문제가 될 수 있다고 한다,,,

 

1. 앱 스토어 심사에서 거부될 가능성

 

: Apple은 앱 스토어 심사 과정에서  앱의 안전성, 보안성, 일관성을 보장하기 위해 비공개 API의 사용을 엄격히 금지하고 있다. 비공개 API를 사용하는 앱은 심사에서 거부될 가능성이 높다.

 

2. 미래 호환성 문제

 

: 비공개 API는 문서화되지 않았기 때문에 Apple이 언제든지 변경하거나 제거할 수 있다. 이는 앱이 향후 iOS 업데이트에서 작동하지 않게 만들 수 있다.

 

3. 안전성 문제

 

:비공개 API는 공식적으로 지원되지 않기 때문에, 예상치 못한 동작이나 버그가 발생할 수 있다. 

 

결론적으로,,, 사용하지 않는 것이 좋다 ㅎ 

이런 비공개 API가 있다는 걸 몰랐는데 지금이라도 알아서 다행이다. 나중에 앱 심사 올리고 난 뒤에 알았으면 타격이 컸을듯 ㅜㅜ

 

 

그럼 비공개 API 사용하지 않고 다시 구현해보자!!

 

 

🟢 다시 구현

 

 

모델명을 출력하는 것에서 비공개 API를 사용했으니 이 부분 코드만 고쳐보았다.

 

sysctl를 사용하여 구현하는데 sysctl는 Unix 계열 운영체제에서 커널 매개변수를 조작하고 시스템 정보를 얻기 위해 사용되는 함수라고 한다.

 

iOS에서도 sysctl을 통해 기기 모델명, CPU 정보 등 다양한 시스템 정보를 안전하게 얻을 수 있다.

 

import SwiftUI


class DeviceInfoHandler: ObservableObject {
    @Published var osVersion: String
    @Published var deviceModel: String
    @Published var appVersion: String

    init() {
        self.osVersion = UIDevice.current.systemVersion
        self.appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
        self.deviceModel = DeviceInfoHandler.getModelName()
    }

    static func getModelName() -> String {
        var systemInfo = utsname()
        uname(&systemInfo)
        let modelCode = withUnsafePointer(to: &systemInfo.machine) { ptr in
            return String(cString: UnsafeRawPointer(ptr).assumingMemoryBound(to: CChar.self))
        }

        let modelMap: [String: String] = [
            "iPhone13,4": "iPhone 12 Pro Max",
            "iPhone13,3": "iPhone 12 Pro",
            "iPhone13,2": "iPhone 12",
            "iPhone13,1": "iPhone 12 Mini",
            // 필요한 모델 코드와 이름 추가
        ]

        return modelMap[modelCode] ?? modelCode
    }
}

 

코드의 흐름은 아래와 같다.

 

  1. 시스템 정보 가져오기: uname(&systemInfo)를 사용하여 시스템 정보를 가져온다.
  2. 모델 코드 추출: systemInfo.machine에서 모델 코드를 추출한다.
  3. 모델 코드 매핑: modelMap 사전을 사용하여 모델 코드를 사람이 읽을 수 있는 이름으로 변환한다.

 

이렇게 고침으로써 비공개 API 사용을 없애고 안전성 문제를 해결할 수 있었다!!

모델명을 모두 정의해야 하는 점이 귀찮긴한데,, 한번 정의만 해두면 괜찮으니까 ㅎ

 

결과적으로 사용자 기기 모델명, OS/앱 버전을 모두 출력해볼 수 있었고, 앞으로 필요한 경우에 따라서 사용하면 될 것 같다!

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
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 29 30 31
글 보관함