Swiftでデバイスまたはシミュレーター用にアプリがビルドされているかどうかを検出する方法


277

Objective-Cでは、マクロを使用してデバイスまたはシミュレーター向けにアプリが構築されているかどうかを確認できます。

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

これらはコンパイル時マクロであり、実行時には使用できません。

Swiftで同じことをするにはどうすればよいですか?


2
それは、Objective-Cで実行時にシミュレータや実​​際のデバイスを検出する方法ではありません。これらは、ビルドに応じて異なるコードを生成するコンパイラディレクティブです。
rmaddy 14

ありがとう。質問を編集しました。
RaffAl 2014

9
最高投票の回答は、この問題を解決するための最良の方法ではありません!mbelskyの答え(現時点では非常に遠い)は、落とし穴がない唯一の解決策です。AppleのGreg Parkerでさえ、そのようにすることを提案しました。lists.swift.org
pipermail

1
大文字の場合でも、実行時チェックで問題があることを示唆するのは初心者です。アップルのエンジニアによる提案は、ごみがよく考えられていないか、特定の状況にしか当てはまらない場合が多いので、最初は何もないという意味ではありません。
Fattie 2017

1
@Fattie:与えられた答えがどれもあなたのニーズを満たさない理由、そしてあなたが報奨金を提供することによってあなたがまさに望んでいることを知ることは興味深いでしょう。
マーティンR

回答:


363

更新30/01/19

この答えはうまくいくかもしれませんが、静的チェックの推奨される解決策(いくつかのAppleエンジニアによって明らかにされています)は、iOSシミュレータをターゲットとするカスタムコンパイラフラグを定義することです。それを行う方法の詳細な手順については、@ mbelskyの回答を参照してください。

元の答え

静的チェックが必要な場合(ランタイムのif / elseなどではない)、シミュレータを直接検出することはできませんが、次のようにデスクトップアーキテクチャでiOSを検出できます。

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

Swift 4.1バージョン以降

すべてのタイプのシミュレーターが1つの条件のみを適用する必要があるため、最新の使用法がオールインワン条件に直接適用されます-

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

詳細については、Swiftの提案SE-0190を確認してください。


古いバージョンの場合 -

明らかに、これはデバイスではfalseですが、ドキュメントで指定されているように、iOSシミュレーターではtrueを返します。

コードが32ビットiOSシミュレーター用にコンパイルされると、arch(i386)ビルド構成は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以下)を検査できます。これはシミュレーターで真実です。

これはプリプロセッサフ​​ラグを使用する場合とは異なり、多少制限があることに注意してください。たとえば、a if/elseが構文的に無効な場所(たとえば関数スコープの外)で使用することはできません。

たとえば、デバイスとシミュレータに異なるインポートを設定するとします。これは、動的チェックでは不可能ですが、静的チェックでは簡単です。

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

また、フラグはSwiftプリプロセッサによってa 0またはaに置き換えられるため、式1で直接使用するとif/else、コンパイラは到達不能コードに関する警告を表示します。

この警告を回避するには、他の回答のいずれかを参照してください。


1
詳細はこちら。さらに制限するために、を使用できますarch(i386) && os(iOS)
ahruss 14

1
これは私にはうまくいきませんでした。私はi386とx86_64の両方をチェックしなければなり
ませんでした

3
これはこの問題を解決するための最良の方法ではありません!mbelskyの答え(現時点では非常に遠い)は、落とし穴がない唯一の解決策です。AppleのGreg Parkerでさえ、そのようにすることを提案しました。lists.swift.org
pipermail

2
@russbishopこれは、これまでに何百人もの人々に役立つアドバイスであり、不足しているAPIを補うものでした。上部のコメントに署名して答えを乗っ取るのではなく、ただ通信するだけです。これが最新のソリューションではないことを明確にするために回答を更新し、より正確に見えるソリューションへのリンクを提供しました。
ガブリエレペトロネラ2017

9
スウィフト4.1では、あなたが言うことができるでしょう#if targetEnvironment(simulator):)(github.com/apple/swift-evolution/blob/master/proposals/...
ハミッシュ

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
これが最良の答えです。AppleのGreg Parkerでさえもそのように提案しました。lists.swift.org
pipermail

1
swift 3の使用方法の更新:UIDevice.current.isSimulator
tylernol

1
[ リリース]でこれを追加すると機能しないのはなぜですか?
William Hu

3
これが唯一の正解です。また、このセットアップを設定することができますxcconfig使用してファイルOTHER_SWIFT_FLAGS = TARGET_OS_EMBEDDEDOTHER_SWIFT_FLAGS[sdk=embeddedsimulator*] = TARGET_OS_SIMULATORシミュレータのために上書きします。
russbishop 2017

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
受け入れられたものよりもはるかに良い解決策。確かに、ある日(非常にありそうもないことですが)、AppleがiOSデバイスでi386またはx85_64を使用することを決定した場合、受け入れられた答えは機能しません...または、デスクトップコンピューターが新しいプロシージャを取得した場合でも!
Frizlab、2016

2
これがXcode 7で完全に機能することを確認しましたpublic let IS_SIMULATOR = (TARGET_OS_SIMULATOR != 0)。+1感謝
Dan Rosenstark

1
@danielこれはうまく機能し、実際には私のソリューションよりも簡単です。ただし、実際のプリプロセッサステップよりも制限されていることに注意してください。コードの一部をターゲットに含めないようにする必要がある場合(たとえば、コンパイル時に2つのインポートから選択する場合)、静的チェックを使用する必要があります。この違いを強調するために、回答を編集しました。
Gabriele Petronella 2016

これはこの問題を解決するための最良の方法ではありません!mbelskyの答え(現時点では非常に遠い)は、落とし穴がない唯一の解決策です。AppleのGreg Parkerでさえ、そのようにすることを提案しました。lists.swift.org
pipermail

2
@Fattie TARGET_OS_SIMULATOR != 0すでに回答に含まれています。それはダニエルによって与えられた解決策です。自由変数に再度追加する必要はありません。すでに存在しています。構造体でそれを使用するのが悪いと思い、自由変数で使用する方が良い場合は、これについてコメントを投稿するか、自分で答えてください。ありがとう。
エリックアヤ

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"
    }
}

Objective-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 /プログラミング言語の更新に基づいて、新しい提案された回答を提案する方法がSOにあったらいいのにと思います。
2018年

4
それは@quemefulの素晴らしいポイントです。SOのいくつかの基本的な障害の1つです。コンピューティングシステムは急速に変化するため、SOのほとんどすべての答えは時間の経過とともに間違ったものになります。
Fattie

40

ここでいくつかのことを明確にしましょう:

  1. TARGET_OS_SIMULATOR多くの場合、Swiftコードでは設定されません。ブリッジングヘッダーが原因で誤ってインポートされる可能性がありますが、これは脆弱であり、サポートされていません。また、フレームワークでは不可能です。これが、これがSwiftで機能するかどうか混乱している理由です。
  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を使用すると思います。このチェックはIMOよりハックに思えます。
ダニエル

「予期しないプラットフォーム条件(予期される「os」、「arch」、または「swift」)がある場合」targetEnvironment(simulator)を使用したエラー
Zaporozhchenko Oleksandr

@Aleksandr targetEnvironmentはXcode 9.3に導入されました。新しいバージョンのXcodeが必要です。
russbishop 2018

@russbishop良い仕事、これを最新の新時代に向けて片付けます-ありがとう!
Fattie

私は250の賞金を送りました。この回答が最新の情報を追加するようです-乾杯
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 2017年

警告が表示されますが、ビルド用に選択したシミュレータまたはデバイスがあるかどうかに応じて、警告は実行されない部分に表示されますが、警告ポリシーがゼロの場合はうんざりです
Fonix

1
== 0代わりに使用したときにのみ警告が表示されます!= 0。上記のようにそれを使用したelse後、ブロックを続けても、Swift 4 Xcodeバージョン9.2(9C40b)で警告は生成されません
shim

また、シミュレータのターゲットと物理デバイスで実行してテストしました。Swift 3.2(同じXcodeバージョン)でも同じようです。
シム

Xcode 9.3 + Swift 4.1では、!= 0でも警告が表示されることに気づきました。シーッシュ。
シム2018

10

最新のシステムでは:

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

簡単です。


1
最初の質問ダニエルの回答よりも「もっと正確」である理由がわかりません。– 2番目コンパイル時のチェックです。明けましておめでとうございます!
マーティン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 2015

iOS 9ヘッダーを見ています。回答を更新します。
Nuthatch、2015

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(およびそれ以前のバージョンではテストしていません)では、「Any iOS Simulator」に対してプラットフォーム固有のビルドフラグ「-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 2017年

デバイスで実行しようとすると、警告が表示されます。実行するシミュレータを選択している場合、警告は表示されません。
ak_ninan 2017年

1
非推奨
rcmstark

1

スウィフト4:

現在、ProcessInfoクラスを使用して、デバイスがシミュレータであるかどうか、およびどの種類のデバイスが使用されているかを確認します。

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

しかし、ご存じのように、simModelCodeどの種類のシミュレーターが起動されたかをすぐに理解するのに適したコードではないので、必要に応じて、この別のSOの答えを見て、現在のiPhone /デバイスモデルを特定し、より人間的なものにすることができます。読み取り可能な文字列。


1

上記のHotJardの素晴らしい答えに基づいたXcode 11 Swiftの例を次に示します。これにより、isDeviceBoolが追加され、SIMULATOR_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、Swift 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.