Swift에서 장치 또는 시뮬레이터 용 앱이 빌드되고 있는지 감지하는 방법


277

Objective-C에서는 매크로를 사용하여 장치 또는 시뮬레이터 용으로 앱을 빌드 중인지 알 수 있습니다.

#if TARGET_IPHONE_SIMULATOR
    // Simulator
#else
    // Device
#endif

컴파일 타임 매크로이며 런타임에는 사용할 수 없습니다.

Swift에서 어떻게 동일한 결과를 얻을 수 있습니까?


2
Objective-C에서 런타임에 시뮬레이터 또는 실제 장치를 감지하는 방법은 아닙니다. 빌드에 따라 다른 코드를 생성하는 컴파일러 지시문입니다.
rmaddy

감사. 내 질문을 편집했습니다.
RaffAl

9
가장 높은 투표 응답은이 문제를 해결하기위한 최선의 방법이 아닙니다! mbelsky의 답변 (현재 매우 아래로)은 함정이없는 유일한 솔루션입니다. : 애플에서조차 그렉 파커는 그렇게 할 제안 lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/...
jan.vogt

1
CAPS에서도 런타임 검사로 잘못되었다는 것을 암시하는 것은 순진합니다. Apple 엔지니어의 제안은 종종 잘못 생각한 쓰레기이거나 특정 상황에서만 적용되므로 그 자체로는 아무것도 의미하지 않습니다.
Fattie

1
@Fattie : 주어진 답변 중 어느 것도 귀하의 요구를 충족시키지 못하는 이유와 현상금을 제공함으로써 정확히 무엇을 기대하고 있는지 아는 것이 흥미로울 것입니다.
Martin R

답변:


363

30/01/19 업데이트

이 답변은 효과가 있지만 정적 검사에 권장되는 솔루션은 여러 Apple 엔지니어가 명시한 것처럼 iOS 시뮬레이터를 대상으로하는 사용자 지정 컴파일러 플래그를 정의하는 것입니다. 자세한 방법은 @ mbelsky 's answer을 참조하십시오 .

원래 답변

정적 검사가 필요한 경우 (예 : 런타임 if / else가 아닌 경우) 시뮬레이터를 직접 감지 할 수 없지만 다음과 같은 데스크탑 아키텍처에서 iOS를 감지 할 수 있습니다.

#if (arch(i386) || arch(x86_64)) && os(iOS)
    ...
#endif

스위프트 4.1 버전 이후

모든 유형의 시뮬레이터에 대해 하나의 조건에서 모두 직접 사용하는 최신 사용은 하나의 조건 만 적용하면됩니다.

#if targetEnvironment(simulator)
  // your simulator code
#else
  // your real device code
#endif

자세한 설명을 보려면 Swift 제안서 SE-0190을 확인하십시오.


이전 버전의 경우 -

분명히 이것은 장치에서 거짓이지만 설명서에 지정된대로 iOS 시뮬레이터의 경우 true를 반환합니다 .

arch (i386) 빌드 구성은 코드가 32 비트 iOS 시뮬레이터 용으로 컴파일 될 때 true를 리턴합니다.

iOS 이외의 시뮬레이터를 개발하는 경우 os매개 변수를 간단히 변경할 수 있습니다 . 예 :

watchOS 시뮬레이터 감지

#if (arch(i386) || arch(x86_64)) && os(watchOS)
...
#endif

tvOS 시뮬레이터 감지

#if (arch(i386) || arch(x86_64)) && os(tvOS)
...
#endif

또는 시뮬레이터를 감지 해도

#if (arch(i386) || arch(x86_64)) && (os(iOS) || os(watchOS) || os(tvOS))
...
#endif

런타임 검사에 문제 가 없다면 시뮬레이터에서 TARGET_OS_SIMULATOR변수 인 변수 (또는 TARGET_IPHONE_SIMULATORiOS 8 이하)를 검사 할 수 있습니다 .

이것은 전 처리기 플래그를 사용하는 것과는 다르고 약간 더 제한적입니다. 예를 들어, if/else구문 상 유효하지 않은 위치 (예 : 함수 범위 외부) 에서는 사용할 수 없습니다 .

예를 들어, 장치와 시뮬레이터에서 다른 가져 오기를 원한다고 가정하십시오. 동적 검사에서는 불가능하지만 정적 검사에서는 쉽지 않습니다.

#if (arch(i386) || arch(x86_64)) && os(iOS)
  import Foo
#else
  import Bar
#endif

또한 플래그는 빠른 전처리기에 의해 a 0또는 a 로 대체 1되므로 if/else식에서 직접 사용 하면 컴파일러는 도달 할 수없는 코드에 대한 경고를 발생시킵니다.

이 경고를 해결하려면 다른 답변 중 하나를 참조하십시오.


1
더 자세한 내용은 여기를 참조하십시오 . 그리고 더욱 제한적으로 사용할 수 있습니다 arch(i386) && os(iOS).
ahruss

1
이것은 나를 위해 작동하지 않았습니다. 나는 i386과 x86_64를 모두 확인해야했다
akaru

3
이 답변은이 문제를 해결하는 가장 좋은 방법은 아닙니다! mbelsky의 답변 (현재 매우 아래로)은 함정이없는 유일한 솔루션입니다. : 애플에서조차 그렉 파커는 그렇게 할 제안 lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/...
jan.vogt

2
@russbishop 이것은 지금까지 수백 명의 사람들에게 도움이되는 조언으로, 누락 된 API를 보완했습니다. 맨 위에 주석을 달아 답변을 가로 채지 말고 대화하십시오. 나는 이것이 더 이상 최신 솔루션이 아니라는 것을 분명히하기 위해 답변을 업데이트했으며 더 정확한 것으로 연결되는 링크를 제공했습니다.
가브리엘 페트로 넬라

9
Swift 4.1에서는 다음과 같이 말할 수 있습니다 #if targetEnvironment(simulator):) ( github.com/apple/swift-evolution/blob/master/proposals/… )
Hamish

172

SWIFT의 구식 4.1. #if targetEnvironment(simulator)대신 사용하십시오 . 출처

Swift에서 시뮬레이터를 감지하려면 빌드 구성을 사용할 수 있습니다.

  • 이 구성 정의 -D IOS_SIMULATOR을스위프트 컴파일러 - 사용자 정의 플래그> 기타 스위프트 플래그
  • 이 드롭 다운에서 모든 iOS 시뮬레이터 SDK 를 선택하십시오.드롭 다운 목록

이제이 문장을 사용하여 시뮬레이터를 감지 할 수 있습니다.

#if IOS_SIMULATOR
    print("It's an iOS Simulator")
#else
    print("It's a device")
#endif

또한 UIDevice 클래스를 확장 할 수 있습니다.

extension UIDevice {
    var isSimulator: Bool {
        #if IOS_SIMULATOR
            return true
        #else
            return false
        #endif
    }
}
// Example of usage: UIDevice.current.isSimulator

8
이것이 가장 좋은 대답이어야합니다! 애플의 Greg Parker조차도 그렇게 제안했다 : lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…
jan.vogt

1
스위프트 3에 대한 사용법 업데이트 : UIDevice.current.isSimulator
타일러 놀

1
Release에서 이것을 추가하면 왜 작동하지 않는지 물어볼 수 있습니까?
William Hu

3
이것이 유일한 정답입니다. 및 시뮬레이터를 xcconfig사용 하여 재정 의하여 파일 에서이를 설정할 수도 있습니다 . OTHER_SWIFT_FLAGS = TARGET_OS_EMBEDDEDOTHER_SWIFT_FLAGS[sdk=embeddedsimulator*] = TARGET_OS_SIMULATOR
russbishop

1
Xcode 9.2 에서이 답변은 일부 시간을 컴파일하지 못했습니다. "D"전에 "-"를 제거하면 문제가 해결되었습니다.
Blake

160

2018 년 2 월 20 일 현재 정보 업데이트

@russbishop은 오랫동안 작동하는 것처럼 보이지만이 답변을 "잘못된"것으로 만드는 정식 답변을 가지고있는 것 같습니다.

Swift에서 장치 또는 시뮬레이터 용 앱이 빌드 중인지 감지

이전 답변

@WZW의 답변과 @Pang의 의견을 바탕으로 간단한 유틸리티 구조체를 만들었습니다. 이 솔루션은 @WZW의 답변으로 생성되는 경고를 피합니다.

import Foundation

struct Platform {

    static var isSimulator: Bool {
        return TARGET_OS_SIMULATOR != 0
    }

}

사용법 예 :

if Platform.isSimulator {
    print("Running on Simulator")
}

10
받아 들여진 것보다 훨씬 더 나은 해결책. 실제로 언젠가는 애플 기기가 iOS 기기에서 i386 또는 x85_64를 사용하기로 결정한 경우, 허용 된 답변이 작동하지 않습니다.
Frizlab 2019

2
이것이 Xcode 7에서 완벽하게 작동한다는 것을 확인했습니다 public let IS_SIMULATOR = (TARGET_OS_SIMULATOR != 0)... ... 같은 일, 단순화되었습니다. +1 감사합니다
Dan Rosenstark

1
@daniel 이것은 잘 작동하며 실제로 내 솔루션보다 더 간단합니다. 그러나 실제 전 처리기 단계보다 더 제한적이라는 점은 주목할 가치가 있습니다. 타겟에 포함되지 않는 코드의 일부가 필요한 경우 (예 : 컴파일시 두 가져 오기 중에서 선택하려는 경우) 정적 검사를 사용해야합니다. 이 차이점을 강조하기 위해 답변을 편집했습니다.
Gabriele Petronella

이 답변은이 문제를 해결하는 가장 좋은 방법은 아닙니다! mbelsky의 답변 (현재 매우 아래로)은 함정이없는 유일한 솔루션입니다. : 애플에서조차 그렉 파커는 그렇게 할 제안 lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/...
jan.vogt

2
@Fattie TARGET_OS_SIMULATOR != 0이미 답변에 있습니다. Daniel이 제공 한 솔루션입니다. 자유 변수에 다시 추가 할 필요가 없습니다. 이미 있습니다. 구조체에 포함시키는 것이 좋지 않다고 생각하고 자유 변수에 넣는 것이 더 낫다면 이것에 대한 의견을 게시하거나 직접 답하십시오. 감사.
Eric Aya

69

Xcode 9.3에서

#if targetEnvironment(simulator)

Swift는 유효한 단일 인수 시뮬레이터를 사용하여 새로운 플랫폼 조건 targetEnvironment를 지원합니다. '#if targetEnvironment (simulator)'형식의 조건부 컴파일을 사용하여 빌드 대상이 시뮬레이터 인 경우를 감지 할 수 있습니다. Swift 컴파일러는 기존 os () 및 arch () 플랫폼 조건을 통해 시뮬레이터 환경을 간접적으로 테스트하는 것으로 보이는 플랫폼 조건을 평가할 때 targetEnvironment (simulator) 사용을 감지, 경고 및 제안하려고 시도합니다. (SE-0190)

iOS 9 이상 :

extension UIDevice {
    static var isSimulator: Bool {
        return NSProcessInfo.processInfo().environment["SIMULATOR_DEVICE_NAME"] != nil
    }
}

스위프트 3 :

extension UIDevice {
    static var isSimulator: Bool {
        return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil
    }
}

iOS 9 이전 :

extension UIDevice {
    static var isSimulator: Bool {
        return UIDevice.currentDevice().model == "iPhone Simulator"
    }
}

목표 -C :

@interface UIDevice (Additions)
- (BOOL)isSimulator;
@end

@implementation UIDevice (Additions)

- (BOOL)isSimulator {
    if([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){9, 0, 0}]) {
        return [NSProcessInfo processInfo].environment[@"SIMULATOR_DEVICE_NAME"] != nil;
    } else {
        return [[self model] isEqualToString:@"iPhone Simulator"];
    }
}

@end

2
정의 된 상수를 사용하는 것보다 문자열을 비교하는 것이 더 취약합니다.
마이클 피터슨

@ P1X3L5 당신이 맞아요! 그러나 저는이 방법이 디버그 모드에서 호출되었다고 가정합니다. 너무 견고 할 수는 없지만 프로젝트에 빠르게 추가 할 수 있습니다
HotJard

1
@GantMan 답변 주셔서 감사합니다. 코드를 수정했습니다
HotJard

@ HotJard 좋은,이 will never be executed경고를 생성하지 않습니다
Dannie P

59

스위프트 4

이제 targetEnvironment(simulator)인수로 사용할 수 있습니다 .

#if targetEnvironment(simulator)
    // Simulator
#else
    // Device
#endif

Xcode 9.3 용으로 업데이트


8
이것은 이제 받아 들여질 것입니다. OS / 프로그래밍 언어의 업데이트를 기반으로 새로운 제안 된 답변을 제안하는 방법이 있기를 바랍니다.
quemeful

4
@quemeful의 요점입니다-SO의 몇 가지 기본 실패 중 하나입니다. 컴퓨팅 시스템이 너무 빨리 바뀌기 때문에 SO에 대한 거의 모든 대답이 시간이 지남에 따라 잘못됩니다 .
Fattie

40

여기에 몇 가지 사항을 설명하겠습니다.

  1. TARGET_OS_SIMULATOR많은 경우 스위프트 코드에서 설정되지 않습니다. 브리징 헤더로 인해 실수로 가져 오는 경우가 있지만 취하기 어렵고 지원되지 않습니다. 프레임 워크에서도 가능하지 않습니다. 이것이 일부 사람들이 이것이 스위프트에서 작동하는지 혼란 스럽습니다.
  2. 시뮬레이터 대신 아키텍처를 사용하지 않는 것이 좋습니다.

동적 검사를 수행하려면

확인 ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil은 완벽합니다.

다음 SIMULATOR_MODEL_IDENTIFIER과 같은 문자열을 반환하는지 확인하여 기본 모델을 시뮬레이션 할 수도 있습니다 iPhone10,3.

정적 점검을 수행하려면 다음을 수행하십시오.

Xcode 9.2 및 이전 버전 : 다른 답변에 표시된 것처럼 자체 Swift 컴파일 플래그를 정의하십시오.

Xcode 9.3+는 새로운 targetEnvironment 조건을 사용합니다 :

#if targetEnvironment(simulator)
    // for sim only
#else
    // for device
#endif

1
여기에 새로운 내부 정보가있는 것 같습니다. 매우 도움이되었습니다! 참고 TARGET_OS_SIMULATOR는 앱 및 프레임 워크 코드에서 꽤 오랫동안 작동했습니다. Xcode 9.3 b3에서도 작동합니다. 그러나 이것은 "우연한"것 같습니다. 버머의 종류; 이 방법은 해킹이 가장 적은 것처럼 보입니다. Xcode 9.3 또는 이전 버전에서 컴파일 될 수있는 프레임 워크 코드 제공 업체로서 컴파일러 오류를 방지하기 위해 #if targetEnvironment ...를 #if swift (> = 4.1) 매크로로 래핑해야하는 것처럼 보입니다. 아니면 .... environment [ "SIMULATOR_DEVICE_NAME"]! = nil을 사용하는 것 같습니다. 이 수표는 더 해킹 된 것 같습니다.
다니엘

targetEnvironment (simulator)를 사용하여 "예기치 않은 플랫폼 조건 (예상 된 'os', 'arch'또는 'swift') 오류가있는 경우
Zaporozhchenko Oleksandr

@Aleksandr targetEnvironment는 Xcode 9.3에 착륙했습니다. 최신 버전의 Xcode가 필요합니다.
russbishop

@russbishop 좋은 새로운 최신 시대를 위해 이것을 정리 감사합니다!
Fattie

이 답변에 가장 새로운 정보를 추가하는 것 같습니다-건배
Fattie

15

Swift 1.0 이후로 나에게 도움이되는 것은 arm 이외의 아키텍처를 확인하는 것입니다.

#if arch(i386) || arch(x86_64)

     //simulator
#else 
     //device

#endif

14

런타임이지만 다른 대부분의 솔루션보다 간단합니다.

if TARGET_OS_SIMULATOR != 0 {
    // target is current running in the simulator
}

또는 전 처리기 매크로를 사용하는 부울을 리턴하는 Objective-C 헬퍼 함수를 ​​호출 할 수 있습니다 (특히 프로젝트에서 이미 혼합중인 경우).

편집 : 특히 Xcode 9.3에서 가장 좋은 해결책은 아닙니다. 참조 HotJard의 답변을


3
이 작업을 수행하지만 else 절에서 "실행되지 않습니다"라는 경고가 표시됩니다. 우리는 제로 경고 규칙이 있으므로 :-(
EricS

이 경고를 표시합니다 그러나 당신이 건물 선택 시뮬레이터 또는 장치가있는 경우, 경고가 실행 실 거예요 부분에 표시됩니다 따라 의미가 있지만, 그래 제로 경고 정책에 대한 성가신
Fonix

1
== 0대신 사용할 때 경고 만 표시 됩니다 != 0. 심지어와, 상기 기록으로 사용 else후 블록 스위프트 4 엑스 코드 버전 9.2 (9C40b)에서 경고 발생하지 않는다
시임

또한 실제 장치뿐만 아니라 시뮬레이터 대상에서 실행되는지 테스트했습니다. Swift 3.2 (같은 Xcode 버전)에서도 동일한 것으로 보입니다.
shim

Xcode 9.3 + Swift 4.1에서는! = 0에서도 경고가 표시됩니다. esh.
shim

10

현대 시스템에서 :

#if targetEnvironment(simulator)
    // sim
#else
    // device
#endif

쉬운 일입니다.


1
첫 번째 질문다니엘의 답변 보다 "더 정확한"이유를 잘 모르겠습니다 . – 두 번째 컴파일 시간 확인입니다. 새해 복 많이 받으세요!
Martin R

5

TARGET_IPHONE_SIMULATORiOS 9에서는 더 이상 사용되지 않습니다 TARGET_OS_SIMULATOR. 대체입니다. 또한 TARGET_OS_EMBEDDED사용할 수 있습니다.

에서 TargetConditionals.h :

#if defined(__GNUC__) && ( defined(__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__MACOS_CLASSIC__) )
. . .
#define TARGET_OS_SIMULATOR         0
#define TARGET_OS_EMBEDDED          1 
#define TARGET_IPHONE_SIMULATOR     TARGET_OS_SIMULATOR /* deprecated */
#define TARGET_OS_NANO              TARGET_OS_WATCH /* deprecated */ 

1
TARGET_OS_SIMULATOR를 시도했지만 TARGET_IPHONE_SIMULATOR가 작동하는 동안 Xcode에서 작동하지 않거나 인식되지 않습니다. 위의 iOS 8.0을 구축 중입니다.
CodeOverRide

iOS 9 헤더를보고 있습니다. 답변을 업데이트하겠습니다.
Nuthatch

5

이 확장 기능이 도움이 되길 바랍니다.

extension UIDevice {
    static var isSimulator: Bool = {
        #if targetEnvironment(simulator)
        return true
        #else
        return false
        #endif
    }()
}

용법:

if UIDevice.isSimulator {
    print("running on simulator")
}

@ ChetanKoli, 나는 코드를 짧게하기보다 매우 명확하게 만들려고했기 때문에 누구나 쉽게 이해할 수 있습니다. 편집 내용에 대한 느낌이 확실하지 않습니다.
Lucas Chwe

3

Xcode 7.2 (및 그 이전 버전에서는 테스트 한 적이 없음)에서 "모든 iOS 시뮬레이터"에 대해 플랫폼 별 빌드 플래그 "-D TARGET_IPHONE_SIMULATOR"를 설정할 수 있습니다.

"Swift Compiler-Customer Flags"의 프로젝트 빌드 설정을 확인한 다음 "Other Swift Flags"에서 플래그를 설정하십시오. 빌드 구성 위로 마우스를 가져 가면 '더하기'아이콘을 클릭하여 플랫폼 별 플래그를 설정할 수 있습니다.

이 방법으로 몇 가지 장점이 있습니다. 1) Swift 및 Objective-C 코드에서 동일한 조건부 테스트 ( "# if TARGET_IPHONE_SIMULATOR")를 사용할 수 있습니다. 2) 각 빌드에만 적용되는 변수를 컴파일 할 수 있습니다.

Xcode 빌드 설정 스크린 샷



1

Swift 3에서 아래 코드를 사용했습니다.

if TARGET_IPHONE_SIMULATOR == 1 {
    //simulator
} else {
    //device
}

1
이 작업을 수행하지만 else 절에서 "실행되지 않습니다"라는 경고가 표시됩니다. 경고 규칙이 없으므로 grrrr ....
EricS

장치로 실행하려고 할 때마다 경고가 표시됩니다. 시뮬레이터를 실행하도록 선택한 경우 경고가 표시되지 않습니다.
ak_ninan

1
더 이상 사용되지 않습니다
rcmstark는

1

스위프트 4 :

현재 ProcessInfo 클래스 를 사용하여 장치가 시뮬레이터인지, 어떤 종류의 장치 를 사용하고 있는지 알고 싶습니다.

if let simModelCode = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
            print("yes is a simulator :\(simModelCode)")
}

그러나 아시다시피, simModelCode어떤 종류의 시뮬레이터가 출시되었는지 즉시 이해하기에 편안한 코드는 아닙니다. 필요한 경우이 다른 답변 을보고 현재 iPhone / 장치 모델을 결정하고보다 인간적이어야합니다. 읽을 수있는 문자열.


1

HotJard의 멋진 답변을 기반으로 한 Xcode 11 Swift 예제 는 Bool을 추가하고 이름 대신 사용 합니다. 변수 할당은 각 줄에서 수행되므로 원하는 경우 디버거에서보다 쉽게 ​​검사 할 수 있습니다.isDeviceSIMULATOR_UDID

import Foundation

// Extensions to UIDevice based on ProcessInfo.processInfo.environment keys
// to determine if the app is running on an actual device or the Simulator.

@objc extension UIDevice {
    static var isSimulator: Bool {
        let environment = ProcessInfo.processInfo.environment
        let isSimulator = environment["SIMULATOR_UDID"] != nil
        return isSimulator
    }

    static var isDevice: Bool {
        let environment = ProcessInfo.processInfo.environment
        let isDevice = environment["SIMULATOR_UDID"] == nil
        return isDevice
    }
}

또한 DTPlatformName포함해야하는 사전 항목 이 있습니다 simulator.


0

아래 코드를 사용하십시오.

#if targetEnvironment(simulator)
   // Simulator
#else
   // Device
#endif

작품 Swift 4Xcode 9.4.1


0

Xcode 11, 스위프트 5

    #if !targetEnvironment(macCatalyst)
    #if targetEnvironment(simulator)
        true
    #else
        false        
    #endif
    #endif

0

다른 답변 외에도.

Objective-c에서 TargetConditionals 포함했는지 확인하십시오 .

#include <TargetConditionals.h>

사용하기 전에 TARGET_OS_SIMULATOR.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.