Swift 2'de özel bir mesajla hata / istisna atmanın en basit yolu?


136

Swift 2'de başka birçok dilde yapmaya alışkın olduğum bir şey yapmak istiyorum: özel mesajla bir çalışma zamanı istisnası atmak. Örneğin (Java'da):

throw new RuntimeException("A custom message here")

ErrorType protokolüne uygun numaralandırma türlerini atabilir anlıyorum, ama ben atmak her hata türü için numaralandırma tanımlamak istemiyorum. İdeal olarak, yukarıdaki örneği mümkün olduğunca yakından taklit etmek istiyorum. ErrorType protokolünü uygulayan özel bir sınıf oluşturma içine baktım, ama ben bile bu protokolün gerektirdiğini anlayamıyorum ( belgelere bakın ). Fikirler?


2
Swift 2 atış / yakalama istisna değildir.
zaph

Yanıtlar:


194

En basit yaklaşım tanımlamak için muhtemelen bir özel enumsadece bir ile casebir etti Stringona bağlı:

enum MyError: ErrorType {
    case runtimeError(String)
}

Veya Swift 4'ten itibaren:

enum MyError: Error {
    case runtimeError(String)
}

Örnek kullanım şöyledir:

func someFunction() throws {
    throw MyError.runtimeError("some message")
}
do {
    try someFunction()
} catch MyError.runtimeError(let errorMessage) {
    print(errorMessage)
}

Varolan Errortürleri kullanmak istiyorsanız , en genel olanı bir olur NSErrorve özel bir mesajla bir tane oluşturmak ve atmak için bir fabrika yöntemi yapabilirsiniz.


Merhaba, bu cevabı gönderdiğin bir yıl oldu biliyorum, ama Stringiçeri girmenin mümkün olup olmadığını bilmek istiyorum errorMessage, eğer öyleyse, bunu nasıl yaparım?
Renan Camaforte

1
@RenanCamaforte Üzgünüm, soruyu anlamıyorum? StringBurada ilişkilidir MyError.RuntimeError(sürenin en seti throw) ve bu öğeye erişmek catch(ile let errorMessage).
Arkku

1
Sizden en basit çözümü istediniz. Özel numaralandırmalar oluşturduğunuzda, işlevler vb. Basit değildir. En az bir yol biliyorum ama orada yayınlamayacağım çünkü objektif-C için
Vyachaslav Gerchicov

3
@VyachaslavGerchicov Eğer soruda da belirtilmiş olan Swift için daha basit bir yol bilmiyorsanız, bu en basit yol olurdu , hatta Objective-C'yi içeren daha genel bir bağlamda bunu basit olarak görmüyorsunuz. . (Ayrıca, bu cevap temel olarak bir
enum'un

1
@Otar Evet, ama… siz bahsediyorsunuz try!, burada kullanılmıyor. Gerçekten de, bir çeşit olmadan potansiyel fırlatma aramasını bile yapamazsınız try. (Ayrıca kodun bu kısmı gerçek çözüm değil örnek kullanımdır.)
Arkku

136

En basit yol aşağıdakilere Stringuymaktır Error:

extension String: Error {}

Sonra sadece bir dize atabilirsiniz:

throw "Some Error"

Dizenin kendisini localizedStringhatanın kendisi yapmak için bunun yerine genişletebilirsiniz LocalizedError:

extension String: LocalizedError {
    public var errorDescription: String? { return self }
}

Bu zekidir, ancak localizedDescriptiondizenin kendisi olmasını sağlamanın bir yolu var mı?
villapossu

1
Çok zarif bir yol!
Vitaliy Gozhenko

1
Gerçekten zarif! Ama test mesajlarında benim için şu mesajla yıkılıyor Redundant conformance of 'String' to protocol 'Error':(
Alexander Borisenko

2
Nedense bu benim için işe yaramıyor. error.localizedDescriptionDize attıktan sonra ayrıştırırken işlemi tamamlayamayacağını söylüyor .
Noah Allen

1
Uyarı: Bu uzantı benim için harici kütüphanelerde sorunlara neden oldu. İşte benim örneğim . Bu, Hataları yöneten herhangi bir 3. taraf kitaplığı için mümkündür; String'i Hataya uygun hale getiren uzantılardan kaçınırım.
Bryan W. Wagner

20

@ nick-keets'in çözümü en zarif, ama benim için test derlemesinde aşağıdaki derleme zamanı hatasıyla yıkıldı:

Redundant conformance of 'String' to protocol 'Error'

İşte başka bir yaklaşım:

struct RuntimeError: Error {
    let message: String

    init(_ message: String) {
        self.message = message
    }

    public var localizedDescription: String {
        return message
    }
}

Ve kullanmak için:

throw RuntimeError("Error message.")

19

Bu harika sürümü kontrol edin. Buradaki fikir, hem String hem de ErrorType protokollerini uygulamak ve hatanın rawValue'unu kullanmaktır.

enum UserValidationError: String, Error {
  case noFirstNameProvided = "Please insert your first name."
  case noLastNameProvided = "Please insert your last name."
  case noAgeProvided = "Please insert your age."
  case noEmailProvided = "Please insert your email."
}

Kullanımı:

do {
  try User.define(firstName,
                  lastName: lastName,
                  age: age,
                  email: email,
                  gender: gender,
                  location: location,
                  phone: phone)
}
catch let error as User.UserValidationError {
  print(error.rawValue)
  return
}

Hala as User.UserValidationErrorve üstünde olmak gerekirse, bu yaklaşımda çok az fayda var gibi görünüyor .rawValue. Yerine uygulanan Ancak, CustomStringConvertibleolarak var description: String { return rawValue }, içinden gitmek zorunda kalmadan enum sözdizimi kullanarak özel açıklamaları almak yararlı olabilir rawValuebunu yazdırmak nerede her yerde.
Arkku

1
dönmek için localizedDescription yöntemini daha iyi uygulayın .rawValue
DanSkeel

16

Hızlı 4:

Başı:

https://developer.apple.com/documentation/foundation/nserror

özel bir istisna tanımlamak istemiyorsanız, standart bir NSError nesnesini aşağıdaki gibi kullanabilirsiniz:

import Foundation

do {
  throw NSError(domain: "my error description", code: 42, userInfo: ["ui1":12, "ui2":"val2"] ) 
}
catch let error as NSError {
  print("Caught NSError: \(error.localizedDescription), \(error.domain), \(error.code)")
  let uis = error.userInfo 
  print("\tUser info:")
  for (key,value) in uis {
    print("\t\tkey=\(key), value=\(value)")
  }
}

Baskılar:

Caught NSError: The operation could not be completed, my error description, 42
    User info:
        key=ui1, value=12
        key=ui2, value=val2

Bu, özel bir dize, ayrıca, ihtiyacınız olan tüm ek verileri içeren bir sayısal kod ve sözlük sağlamanıza olanak tanır.

Not: Bu, OS = Linux (Ubuntu 16.04 LTS) üzerinde test edilmiştir.


12

Ekstra uzantı, numaralandırma, sınıf vb. İçermeyen en basit çözüm:

NSException(name:NSExceptionName(rawValue: "name"), reason:"reason", userInfo:nil).raise()

2
yeniden. Benim yanıta Yorumlarınızı, bu biraz keyfi tanımlanması ve numaralandırma veya uzatma karar verdik sadece anlamda basittir kez karmaşıktır. Yani, evet, cevabın "kurulum" sıfır çizgileri vardır, ama sahip pahasına her atılan istisnası karmaşık ve (non-Swiftlike olmak raise()yerine throw) büyü olduğunu hatırlamak zordur. Çözümünüzü, bir istisnanın atıldığı yer sayısıyla karşılaştırın throw Foo.Bar("baz")veya throw "foo"bu sayıyla çarpın - IMO, tek satırlık uzantı veya numaralandırma için bir kerelik ücret gibi şeylere çok tercih edilir NSExceptionName.
Arkku

@Arkku Örneğin postNotification2-3 parametre gerektirir ve seçici buna benzer. Her projede, daha az girdi parametresi kabul etmesine izin vermek için geçersiz kılıyor Notificationve / veya NotificationCenterher projede mi?
Vyachaslav Gerchicov

1
Hayır, çözümü kendi cevabımda bile kullanmam; Sadece soruyu cevaplamak için yayınladım, çünkü kendim yapacağım bir şey değil. Her neyse, konunun yanı sıra: Cevabınızın, benimkinden ya da Nick Keets'inkinden çok daha karmaşık olduğu kanaatindeyim. Elbette, Stringuymak için uzatmanın Errorçok şaşırtıcı olması veya bir MyErrorenumun çok belirsiz olması (kişisel olarak her ikisine de evet yanıtı veririm ve bunun yerine her hata için ayrı bir numaralandırma durumu yapar gibi) dikkate alınması gereken başka geçerli noktalar da vardır. throw ThisTypeOfError.thisParticularCase).
Arkku

6

@Nick keets cevabına dayanarak, daha eksiksiz bir örnek:

extension String: Error {} // Enables you to throw a string

extension String: LocalizedError { // Adds error.localizedDescription to Error instances
    public var errorDescription: String? { return self }
}

func test(color: NSColor) throws{
    if color == .red {
        throw "I don't like red"
    }else if color == .green {
        throw "I'm not into green"
    }else {
        throw "I like all other colors"
    }
}

do {
    try test(color: .green)
} catch let error where error.localizedDescription == "I don't like red"{
    Swift.print ("Error: \(error)") // "I don't like red"
}catch let error {
    Swift.print ("Other cases: Error: \(error.localizedDescription)") // I like all other colors
}

Başlangıçta hızlı blogumda yayınlandı: http://eon.codes/blog/2017/09/01/throwing-simple-errors/


1
TBH: Şimdi yapıyorumthrow NSError(message: "err", code: 0)
eonist

Yani kendi örneğinizi bile kullanmıyor musunuz? : D Oh, ve ilk argüman olmalı domain, değil messagemi?
NRitH

1
Hakkınız, etki alanı. Ve hayır, koda çok fazla şeker ekliyor. Genellikle çok sayıda küçük çerçeve ve modül yaparım ve uygun şekerleme şekerini düşük tutmaya çalışırım. Bugünlerde Sonuç ve NSError arasında bir karışım kullanmaya çalışıyorum
eonist

6

Hatayı yakalamanız gerekmiyorsa ve uygulamayı hemen durdurmak istiyorsanız bir fatalError kullanabilirsiniz: fatalError ("Custom message here")


3
Bunun yakalanabilecek bir hata atmayacağını unutmayın. Bu uygulama çökecektir.
Adil Hussain

4

@ Alexander-Borisenko'nun cevabını seviyorum, ancak Hata olarak yakalandığında yerelleştirilmiş açıklama döndürülmedi. Bunun yerine LocalizedError kullanmanız gerekiyor gibi görünüyor:

struct RuntimeError: LocalizedError
{
    let message: String

    init(_ message: String)
    {
        self.message = message
    }

    public var errorDescription: String?
    {
        return message
    }
}

Daha fazla ayrıntı için bu cevaba bakınız.

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.