Развертывание нескольких опций в swift

Я хочу загрузить PDF, который находится в моем комплекте приложений, в CGPDFDocument.

Есть ли способ вызова функции, если любой из параметров, которые не принимают параметры, имеет значения, равные nil, функция не вызывается и возвращается nil.

например:

let pdfPath : String? = NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf")
//I want to do this
let data : NSData? = NSData(contentsOfFile:pdfPath)
//I have to do this
let data : NSData? = pdfPath != nil ? NSData(contentsOfFile:pdfPath) : nil
let doc : CGPDFDocumentRef? = CGPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data));
//pageView.pdf is optional, nicely this function accepts the document as an optional
pageView.pdfPage = CGPDFDocumentGetPage(doc, 1);

Поскольку NSData.init?(ContentsOfFile path:String), не определяет path как необязательный, даже если он имеет необязательное возвращаемое значение, я должен проверить раньше, и если параметр равен nil, вернуть nil. Есть ли синтаксический сахар для data назначение (вместо ?: оператор)?

4 ответа

Либо используйте несколько необязательных привязок, разделенных запятыми

func loadPDF() -> CGPDFDocumentRef? 
{
  if let pdfPath = NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf"),
       data = NSData(contentsOfFile:pdfPath), 
       doc = GPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data)) {
    return doc
  } else {
    return nil
  }
}

или используйте guard заявление

func loadPDF() -> CGPDFDocumentRef? 
{
  guard let pdfPath = NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf") else { return nil }
  guard let data = NSData(contentsOfFile:pdfPath) else { return nil }
  return GPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data))
}

Все явные аннотации типов являются синтаксическим сахаром и не нужны.

Редактировать:

В вашем конкретном случае вам нужно только проверить, существует ли файл, и даже это - файл отсутствует - очень маловероятно в iOS. Еще одним преимуществом является возможность вернуть необязательный PDFDocument.

func loadPDF() -> CGPDFDocumentRef
{
  guard let pdfPath = NSBundle.mainBundle().pathForResource("nac_06", ofType:"pdf") else {
    fatalError("file nac_06.pdf does not exist")
  }
  let data = NSData(contentsOfFile:pdfPath)
  return CGPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data!))!
}

Есть два способа добиться этого.

  1. простираться NSData класс и создать свой собственный convenience init? метод
  2. Использовать guard заявление

Я предпочитаю второй метод:

func getPDF(path : String?) -> CGPDFDocumentRef?
{
    guard let filePath = path,
              data     = NSData(contentsOfFile: filePath),
              pdf      = GPDFDocumentCreateWithProvider(CGDataProviderCreateWithCFData(data)) else
    {
        return nil
    }
    return pdf
}

Вызовите метод как:

let doc = getPDF(path : NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf"))

Вы можете сделать что-то необычное, определив пользовательский оператор для решения этой ситуации. Например:

infix operator ^> {associativity left precedence 150}

func ^><T, U>(arg: T?, f: T->U?) -> U?{
    if let arg = arg {
        return f(arg)
    } else {
        return nil
    }
}

Оператор принимает необязательный аргумент левой стороны и функцию, которая принимает необязательный аргумент и возвращает другой необязательный аргумент в качестве аргумента правой стороны.

Затем вы можете написать свой код так:

let pdfPath = NSBundle.mainBundle().pathForResource("nac_06", ofType:"pdf")
//the line below needs a NSData extension
let data = pdfPath ^> NSData.fileContents
let doc = data ^> CGDataProviderCreateWithCFData ^> CGPDFDocumentCreateWithProvider
//pageView.pdf is optional, nicely this function accepts the document as an optional
pageView.pdfPage = CGPDFDocumentGetPage(doc, 1)

Обратите внимание, что для этого вам нужно добавить расширение к NSData, так как вы не можете отобразить init(contentsOfFile:) инициализатор для универсальной функции, которая может быть передана ^>,

extension NSData {
    class func fileContents(path: String) -> NSData? {
        return NSData(contentsOfFile: path)
    }
}

Использование ^> Оператор, однако, восстанавливает порядок, в котором вы пишете имена функций. Если вы предпочитаете, чтобы имена функций были в том же порядке, что и исходный код, вы можете добавить обратный оператор, который делает то же самое:

infix operator ^< {associativity right precedence 150}

func ^< <T, U>(f: T->U?, arg: T?) -> U?{
    if let arg = arg {
        return f(arg)
    } else {
        return nil
    }
}

let pdfPath = NSBundle.mainBundle().pathForResource("nac_06", ofType:"pdf")
let data = NSData.fileContents ^< pdfPath
let doc = CGPDFDocumentCreateWithProvider ^< CGDataProviderCreateWithCFData ^< data
//pageView.pdf is optional, nicely this function accepts the document as an optional
pageView.pdfPage = CGPDFDocumentGetPage(doc, 1)

Я предполагаю, что вы также не хотите продолжать выполнение функции, если pdfPath или же data ноль В этом случае, guard будет лучшим выбором:

guard let pdfPath = NSBundle.mainBundle().pathForResouce("nac_06", ofType:"pdf") else {
    // eventually also report some error
    return
}

guard let data = NSData(contentsOfFile:pdfPath) else {
    // eventually also report some error
    return
}

// at this point you have a valid data object

Вы также можете объединить это в один guard утверждение, чтобы уменьшить дублирование кода, вы потеряете, однако, в этом случае возможность узнать, какой из двух не удалось.

guard let pdfPath = NSBundle.mainBundle().pathForResource("nac_06", ofType:"pdf"),
    data = NSData(contentsOfFile:pdfPath) else {
        // eventually also report some error
        return
}
Другие вопросы по тегам