Uygulamanın Swift'te cihaz veya simülatör için oluşturulup oluşturulmadığını belirleme


277

Objective-C'de bir uygulamanın makrolar kullanılarak cihaz veya simülatör için oluşturulup oluşturulmadığını öğrenebiliriz:

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

Bunlar derleme zamanı makrolarıdır ve çalışma zamanında kullanılamaz.

Aynı şeyi Swift'te nasıl başarabilirim?


2
Objective-C'de çalışma zamanında simülatörü veya gerçek bir cihazı tespit etmek bu değildir. Bunlar derlemeye bağlı olarak farklı kodlarla sonuçlanan derleyici yönergeleridir.
rmaddy

Teşekkürler. Sorumu düzenledim.
RaffAl

9
EN YÜKSEK OYUN YANITLARI BU SORUNU ÇÖZME İÇİN EN İYİ YOL DEĞİLDİR! mbelsky'nin cevabı (şu anda çok uzaklarda) herhangi bir tuzak olmadan gelen tek çözümdür. Apple'dan Greg Parker bile bu şekilde yapılmasını önerdi: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…
jan.vogt

1
CAPS'TA BİLE, ÇALIŞMA SÜRESİNDE YANLIŞ BİR ŞEY OLDUĞUNU ÖNERİYOR. Apple mühendislerinin önerileri genellikle kötü düşünülmüş çöplerdir veya sadece belirli durumlarda geçerlidir, böylece initself hiçbir şeyden daha az anlamına gelir.
Aralık'ta Fattie

1
@Fattie: Verilen cevapların hiçbirinin neden ihtiyaçlarınızı karşılamadığını ve ödül teklif ederek tam olarak neyi umduğunuzu bilmek ilginç olurdu.
Martin R

Yanıtlar:


364

Güncelleme 30/01/19

Bu yanıt işe yarasa da, statik bir kontrol için önerilen çözüm (birkaç Apple mühendisi tarafından açıklandığı gibi) iOS Simülatörlerini hedefleyen özel bir derleyici bayrağı tanımlamaktır. Bunun nasıl yapılacağı hakkında ayrıntılı talimatlar için @ mbelsky'nin cevabına bakın .

Orijinal cevap

Statik bir kontrole ihtiyacınız varsa (örneğin / else ise bir çalışma zamanı değil) simülatörü doğrudan algılayamazsınız, ancak iOS'u aşağıdaki gibi bir masaüstü mimarisinde tespit edebilirsiniz.

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

Swift 4.1 sürümünden sonra

Son kullanım, şimdi doğrudan herkes için tek bir koşulda tüm simülatörler için sadece bir koşul uygulamak gerekir -

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

Daha fazla açıklama için Swift teklifini SE-0190 kontrol edebilirsiniz.


Eski sürüm için -

Açıkçası, bu bir cihazda yanlıştır, ancak belgelerde belirtildiği gibi iOS Simülatörü için doğrudur :

Arch (i386) derleme yapılandırması, kod 32 bit iOS simülatörü için derlendiğinde true değerini döndürür.

İOS dışında bir simülatör için geliştiriyorsanız, osparametreyi değiştirebilirsiniz: ör.

WatchOS simülatörünü algılama

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

TVOS simülatörünü algılama

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

Veya herhangi bir simülatörü tespit edin

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

Bunun yerine bir çalışma zamanı denetimi ile sorun yaşıyorsanız, bir simülatörde doğru olan TARGET_OS_SIMULATORdeğişkeni (veya TARGET_IPHONE_SIMULATORiOS 8 ve aşağısında) inceleyebilirsiniz.

Bunun bir önişlemci bayrağı kullanmaktan farklı ve biraz daha sınırlı olduğunu lütfen unutmayın. Örneğin if/elsea'yı sözdizimsel olarak geçersiz olduğu yerde kullanamazsınız (örneğin işlev kapsamlarının dışında).

Örneğin, cihazda ve simülatörde farklı ithalatlar yapmak istediğinizi varsayalım. Bu, dinamik bir kontrol ile imkansızdır, oysa statik bir kontrol ile önemsizdir.

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

Ayrıca, bayrak bir 01 hızlı önişlemci tarafından veya a , doğrudan bir if/elseifadede kullanırsanız, derleyici ulaşılamaz kod hakkında bir uyarı verecektir.

Bu uyarıya geçici bir çözüm bulmak için diğer yanıtlardan birine bakın.


1
Burada daha fazla okuma . Ve daha da kısıtlayıcı olabilmek için kullanabilirsiniz arch(i386) && os(iOS).
ahruss

1
Bu benim için işe yaramadı. Ben hem i386 hem de x86_64 için kontrol etmek zorunda kaldım
akaru

3
BU CEVAP BU SORUNU ÇÖZME İÇİN EN İYİ YOL DEĞİLDİR! mbelsky'nin cevabı (şu anda çok uzaklarda) herhangi bir tuzak olmadan gelen tek çözümdür. Apple'dan Greg Parker bile bu şekilde yapılmasını önerdi: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…
jan.vogt

2
@russbishop, şimdiye kadar yüzlerce kişiye eksik bir API'yı telafi ederek faydalı tavsiyeler olduğunu kanıtladı. Üstte bir yorum imzalayarak cevabı kaçırmak yerine, sadece iletişim kurun. Bunun artık güncel bir çözüm olmadığını açıklığa kavuşturmak için cevabı güncelledim ve daha doğru görünen çözümün bağlantısını verdim.
Gabriele Petronella

9
Swift 4.1'de şunları söyleyebileceksiniz #if targetEnvironment(simulator):) ( github.com/apple/swift-evolution/blob/master/proposals/… )
Hamish

172

SWIFT İÇİN GEÇTİ #if targetEnvironment(simulator)Bunun yerine kullanın . Kaynak

Swift'te simülatörü tespit etmek için derleme yapılandırmasını kullanabilirsiniz:

  • Bu yapılandırmayı tanımla -D IOS_SIMULATOR içinde Swift Compiler - Özel Flags> Diğer Swift Flags
  • Bu açılır menüden Herhangi Bir iOS Simülatörü SDK'sını seçinAçılır liste

Şimdi simülatörü tespit etmek için bu ifadeyi kullanabilirsiniz:

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

Ayrıca UIDevice sınıfını genişletebilirsiniz:

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

8
Bu en iyi cevap olmalı! Apple'dan Greg Parker bile şu yolu önerdi: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…
jan.vogt

1
3 için kullanım güncellemesi: UIDevice.current.isSimulator
tylernol

1
Bunu Sürüm altına eklersem neden işe yaramaz?
William Hu

3
Tek doğru cevap bu. Bunu Simülatör için geçersiz kılmak için ve düğmelerini xcconfigkullanarak dosyalarda da ayarlayabilirsiniz . OTHER_SWIFT_FLAGS = TARGET_OS_EMBEDDEDOTHER_SWIFT_FLAGS[sdk=embeddedsimulator*] = TARGET_OS_SIMULATOR
russbishop

1
Xcode 9.2'de bu yanıt zaman zaman derlenemedi. "D" öncesi "-" işaretini kaldırmak benim için sorunu çözdü.
Blake

160

20 Şubat 2018 itibariyle güncellenen bilgiler

Görünüşe göre @russbishop, bu cevabı "yanlış" kılan yetkili bir cevabı var - uzun süre çalışmış gibi görünse de.

Uygulamanın Swift'te cihaz veya simülatör için oluşturulup oluşturulmadığını algılama

Önceki Cevap

@ WZW'nin cevabı ve @ Pang'ın yorumlarına dayanarak, basit bir yardımcı program yapısı oluşturdum. Bu çözüm @ WZW'nin cevabı tarafından üretilen uyarıyı önler.

import Foundation

struct Platform {

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

}

Örnek kullanım:

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

10
Kabul edilenden çok daha iyi bir çözüm. Gerçekten de, bir gün (çok olası olmasa da) Apple, iOS cihazlarda i386 veya x85_64 kullanmaya karar verirse, kabul edilen cevap işe yaramaz… veya masaüstü bilgisayarlar yeni bir süreç elde etse bile!
Frizlab

2
Bunun Xcode 7'de mükemmel çalıştığını doğruladı: public let IS_SIMULATOR = (TARGET_OS_SIMULATOR != 0)... aynı şey, basitleştirilmiş. +1 teşekkürler
Dan Rosenstark

1
@daniel Bu iyi çalışıyor ve aslında benim çözümümden daha basit. Ancak, gerçek bir önişlemci adımından daha sınırlı olduğunu belirtmek gerekir. Kodun bir kısmının hedefe dahil edilmemesine ihtiyacınız varsa (örneğin derleme zamanında iki içe aktarma arasında seçim yapmak istiyorsanız), statik bir kontrol kullanmanız gerekir. Bu farkı vurgulamak için cevabımı düzenledim.
Gabriele Petronella

BU CEVAP BU SORUNU ÇÖZME İÇİN EN İYİ YOL DEĞİLDİR! mbelsky'nin cevabı (şu anda çok uzaklarda) herhangi bir tuzak olmadan gelen tek çözümdür. Apple'dan Greg Parker bile bu şekilde yapılmasını önerdi: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/…
jan.vogt

2
@Fattie TARGET_OS_SIMULATOR != 0olduğu cevabını zaten . Daniel tarafından verilen çözüm. Tekrar ücretsiz bir değişkene eklemenize gerek yok, zaten orada. Bir yapıya sahip olmanın kötü olduğunu ve serbest bir değişkene sahip olmanın daha iyi olduğunu düşünüyorsanız, bu konuda bir yorum gönderin veya kendi cevabınızı verin. Teşekkürler.
Eric Aya

69

Xcode 9.3'ten

#if targetEnvironment(simulator)

Swift, tek bir geçerli argüman simülatörü ile yeni bir platform koşulu targetEnvironment'ı destekler. '#İf targetEnvironment (simulator)' formunun koşullu derlemesi artık oluşturma hedefinin bir simülatör olduğunu algılamak için kullanılabilir. Swift derleyici, mevcut os () ve arch () platform koşulları aracılığıyla dolaylı olarak simülatör ortamları için test edilen platform koşullarını değerlendirirken targetEnvironment (simulator) kullanımını tespit etmeye, uyarmaya ve önermeye çalışacaktır. (SE-0190)

iOS 9+:

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

Hızlı 3:

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

İOS 9'dan önce:

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
Dizeleri karşılaştırmak tanımlı sabitleri kullanmaktan daha kırılgandır.
Michael Peterson

@ P1X3L5 haklısın! Ancak bu yöntemin hata ayıklama modunda çağrıldığını varsayıyorum - o kadar sağlam olamazdı, ancak bir projeye hızlı bir şekilde eklenebilir
HotJard

1
@ Cevap verdiğiniz için teşekkürler. Ben kodu
sabitledim

@HotJard güzel, bu will never be executeduyarı üretmiyor
Dannie P

59

Hızlı 4

Artık targetEnvironment(simulator)argüman olarak kullanabilirsiniz .

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

Xcode 9.3 için güncellendi


8
Bu şimdi kabul edilen cevap olmalı. SO'da işletim sistemi / programlama dillerindeki güncellemelere dayalı yeni bir önerilen cevap önermenin bir yolu olsaydı.
18'de

4
@ quemeful harika bir nokta - SO'nun birkaç temel başarısızlığından biridir. Bilgi işlem sistemleri çok hızlı değiştiğinden , SO üzerindeki hemen hemen her cevap zamanla yanlış olur .
Fattie

40

Burada bazı şeylere açıklık getireyim:

  1. TARGET_OS_SIMULATORçoğu durumda Swift kodunda ayarlanmamıştır; bir köprüleme başlığı nedeniyle yanlışlıkla içe aktarılıyor olabilirsiniz, ancak bu kırılgandır ve desteklenmez. Çerçevelerde bile mümkün değil. Bu yüzden bazı insanlar Swift'te işe yarayıp yaramadığı konusunda kafası karışık.
  2. Simülatörü yerine mimariyi kullanmamanızı şiddetle tavsiye ederim.

Dinamik kontroller yapmak için:

Kontrol ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nilgayet iyi.

SIMULATOR_MODEL_IDENTIFIERHangi modelin hangi dizeleri döndüreceğini kontrol ederek simüle edilmesini de sağlayabilirsiniz iPhone10,3.

Statik kontroller yapmak için:

Xcode 9.2 ve öncesi: kendi Swift derleme bayrağınızı tanımlayın (diğer cevaplarda gösterildiği gibi).

Xcode 9.3+ yeni targetEnvironment koşulunu kullanır:

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

1
Burada yeni bir bilgi var gibi görünüyor. Çok yararlı! TARGET_OS_SIMULATOR uygulamasının hem uygulama hem de çerçeve kodunda bir süre çalıştığını unutmayın; ve Xcode 9.3 b3'te de çalışıyor. Ama sanırım bu "tesadüfi". Bir çeşit bummer; çünkü bu en ufak tefek yol gibi gözüküyor. Xcode 9.3 veya önceki sürümlerde derlenebilecek bir çerçeve kodu sağlayıcısı olarak, derleyici hatalarını önlemek için #if targetEnvironment ... öğesini bir #if swift (> = 4.1) makrosuna sarmamız gerekecek gibi görünüyor. Veya sanırım .... ortamı ["SIMULATOR_DEVICE_NAME"]! = Nil. Bu kontrol daha acayip görünüyor, IMO.
Daniel

eğer hata targetEnvironment (simülatör) kullanılarak "Beklenmeyen platform koşulunu (beklenen 'os', 'kemer', ya da 'swift')" var
Zaporozhchenko Oleksandr

@Aleksandr targetEnvironmentXcode 9.3'e indi. Xcode'un daha yeni bir sürümüne ihtiyacınız var.
russbishop

@russbishop en son yeni çağ için bunu temizleyen iyi çalışmalar - teşekkürler!
Fattie

250 cömert gönderdim, çünkü bu cevap en yeni ve en yeni bilgileri ekliyor gibi görünüyor - şerefe
Fattie

15

Swift 1.0, kol dışında bir mimari kontrol ettiğinden benim için işe yarayan:

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

     //simulator
#else 
     //device

#endif

14

Çalışma zamanı, ancak buradaki diğer çözümlerin çoğundan daha basit:

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

Alternatif olarak, önişlemci makrosunu kullanan bir boole döndüren bir Objective-C yardımcı işlevini çağırabilirsiniz (özellikle projenizde zaten karıştırıyorsanız).

Düzenleme: Özellikle Xcode 9.3 itibariyle en iyi çözüm değil. HotJard'ın cevabına bakın


3
Bunu yapıyorum ama diğer maddede uyarılar alıyorum çünkü "asla yürütülmeyecek". Sıfır uyarı kuralımız var, bu yüzden :-(
EricS

bir uyarı gösterecektir, ancak bina için simülatör veya cihazınız varsa, uyarı yürütülmeyecek kısımda gösterilecektir, ancak sıfır uyarı politikası için sinir bozucu olacaktır
Fonix

1
Uyarıları yalnızca == 0yerine kullandığımda görüyorum != 0. Hatta, yukarıda yazılı olarak kullanılması elsesonra blok Swift 4 Xcode Sürüm 9.2 (9C40b) 'de herhangi bir uyarı üretmez,
takoz

Ayrıca bir simülatör hedefi ve fiziksel bir cihaz üzerinde çalıştığını test ettim. Ayrıca Swift 3.2'de de aynı görünüyor (aynı Xcode sürümü).
shim

Xcode 9.3 + Swift 4.1'de! = 0 ile bile uyarı olduğunu fark ettim. Sheesh.
shim

10

Modern sistemlerde:

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

Bu kolay değil.


1
İlki neden Daniel'in cevabından "daha doğru" olmalı emin değilim . - İkincisi o Not olan bir derleme zamanı çek. Yeni Yılınız mutlu olsun!
Martin R

5

TARGET_IPHONE_SIMULATORiOS 9'da kullanımdan kaldırıldı TARGET_OS_SIMULATOR. Ayrıca TARGET_OS_EMBEDDEDmevcuttur.

Gönderen 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 denedim ama TARGET_IPHONE_SIMULATOR yaparken çalışmıyor veya Xcode tarafından tanınmıyor. Yukarıda iOS 8.0 için geliştiriyorum.
CodeOverRide

İOS 9 başlıklarına bakıyorum. Cevabımı güncelleyeceğim.
Nuthatch

5

Umarım bu uzantı işe yarar.

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

Kullanımı:

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

@ChetanKoli, kısa değil, kodu çok açık hale getirecektim, bu yüzden herkes için anlaşılması kolay. Düzenlemeniz hakkında nasıl hissettiğimden emin değilim.
Lucas Chwe

3

Xcode 7.2'de (ve daha önce ama ne kadar önce test etmedim), "Herhangi bir iOS Simülatörü" için platforma özel bir yapı bayrağı "-D TARGET_IPHONE_SIMULATOR" ayarlayabilirsiniz.

"Swift Derleyici - Müşteri Bayrakları" altındaki proje oluşturma ayarlarına bakın ve "Diğer Swift Bayrakları" altında bayrağı ayarlayın. Bir yapı yapılandırmasının üzerine geldiğinizde 'artı' simgesini tıklayarak platforma özgü bir bayrak ayarlayabilirsiniz.

Bunu bu şekilde yapmanın birkaç avantajı vardır: 1) Swift ve Objective-C kodunuzda aynı koşullu testi ("#if TARGET_IPHONE_SIMULATOR") kullanabilirsiniz. 2) Yalnızca her yapı için geçerli olan değişkenleri derleyebilirsiniz.

Xcode build settings ekran görüntüsü



1

Swift 3'te aşağıdaki kodu kullandım

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

1
Bunu yapıyorum ama diğer maddede uyarılar alıyorum çünkü "asla yürütülmeyecek". Sıfır bir uyarı kuralımız var, bu yüzden grrrr ....
EricS

Bir cihazla çalıştırmaya çalıştığınızda bir uyarı gösterecektir, eğer çalıştırmak için simülatör seçildiyseniz uyarı göstermez.
ak_ninan

1
kullanımdan kaldırıldı
rcmstark

1

Hızlı 4:

Şu anda, cihazın bir simülatör olup olmadığını ve hangi tür cihazın kullanımda olduğunu bilmek için ProcessInfo sınıfını kullanmayı tercih ediyorum :

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

Ancak, bildiğiniz gibi, simModelCodehangi tür simülatörün başlatıldığını hemen anlamak için rahat bir kod değildir, bu nedenle ihtiyacınız varsa , mevcut iPhone / cihaz modelini belirlemek ve daha insani olmak için bu diğer SO yanıtını görmeye çalışabilirsiniz. okunabilir dize.


1

İşte HotJard'ın yukarıdaki harika cevabına dayanan bir Xcode 11 Swift örneği , bu da bir isDeviceBool ekler ve SIMULATOR_UDIDad yerine kullanır . Her satırda değişken atamalar yapılır, böylece isterseniz hata ayıklayıcıda daha kolay bir şekilde inceleyebilirsiniz.

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

Ayrıca sözlük girişi DTPlatformNameiçermelidir simulator.


0

Aşağıdaki kodu kullanın:

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

İçin İşleri Swift 4veXcode 9.4.1


0

Xcode 11, Swift 5

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

0

Diğer cevaplara ek olarak.

Objective-c'de, TargetConditionals'ı eklediğinizden emin olun .

#include <TargetConditionals.h>

kullanmadan önce TARGET_OS_SIMULATOR.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.