grand example dispatchqueue dispatch_async central async ios swift multithreading large-files large-data

ios - dispatchqueue - nsoperationqueue swift 3 example



Cómo escribir eficientemente archivos grandes en el disco en un hilo de fondo(Swift) (3)

Actualizar

He resuelto y eliminado el error de distracción. Por favor, lea la publicación completa y no dude en dejar comentarios si quedan preguntas.

Fondo

Estoy intentando escribir archivos relativamente grandes (video) en el disco en iOS usando Swift 2.0, GCD y un controlador de finalización. Me gustaría saber si hay una forma más eficiente de realizar esta tarea. La tarea debe realizarse sin bloquear la IU principal, mientras se usa la lógica de finalización, y también se asegura de que la operación se realice lo más rápido posible. Tengo objetos personalizados con una propiedad NSData, por lo que actualmente estoy experimentando con una extensión en NSData. Como ejemplo, una solución alternativa podría incluir el uso de NSFilehandle o NSStreams junto con algún tipo de comportamiento seguro de subprocesos que resulta en un rendimiento mucho más rápido que la función NSData writeToURL en la que baso la solución actual.

¿Qué pasa con NSData de todos modos?

Tenga en cuenta la siguiente discusión tomada de la referencia de clase NSData, ( Guardar datos ). Realizo escrituras en mi directorio temporal, sin embargo, la razón principal por la que tengo un problema es que puedo ver un retraso notable en la interfaz de usuario cuando se trata de archivos grandes. Este retraso se debe precisamente a que NSData no es asíncrono (y Apple Docs nota que las escrituras atómicas pueden causar problemas de rendimiento en archivos "grandes" ~> 1 mb). Entonces, cuando se trata de archivos grandes, uno está a merced de cualquier mecanismo interno que funcione dentro de los métodos de NSData.

Hice más excavaciones y encontré esta información de Apple ... "Este método es ideal para convertir datos: // URL a objetos NSData, y también se puede usar para leer archivos cortos de manera sincrónica. Si necesita leer archivos potencialmente grandes , use inputStreamWithURL: para abrir una secuencia, luego lea el archivo pieza a pieza ". ( Referencia de clase NSData, Objective-C, + dataWithContentsOfURL ). Esta información parece implicar que podría intentar usar secuencias para escribir el archivo en un subproceso de fondo si mover el writeToURL al subproceso de fondo (como lo sugiere @jtbandes) no es suficiente.

La clase NSData y sus subclases proporcionan métodos para guardar rápida y fácilmente sus contenidos en el disco. Para minimizar el riesgo de pérdida de datos, estos métodos brindan la opción de guardar los datos de forma atómica. Las escrituras atómicas garantizan que los datos se guardan en su totalidad o fallan por completo. La escritura atómica comienza escribiendo los datos en un archivo temporal. Si esta escritura tiene éxito, entonces el método mueve el archivo temporal a su ubicación final.

Si bien las operaciones de escritura atómica minimizan el riesgo de pérdida de datos debido a archivos dañados o parcialmente escritos, pueden no ser adecuadas al escribir en un directorio temporal, el directorio de inicio del usuario u otros directorios de acceso público. Cada vez que trabaje con un archivo de acceso público, debe tratar ese archivo como un recurso no confiable y potencialmente peligroso. Un atacante puede comprometer o corromper estos archivos. El atacante también puede reemplazar los archivos con enlaces duros o simbólicos, haciendo que sus operaciones de escritura sobrescriban o corrompan otros recursos del sistema.

Evite utilizar el método writeToURL: atomically: (y los métodos relacionados) cuando trabaje dentro de un directorio de acceso público. En su lugar, inicialice un objeto NSFileHandle con un descriptor de archivo existente y use los métodos NSFileHandle para escribir el archivo de forma segura.

Otras alternativas

Un article sobre Programación concurrente en objc.io ofrece opciones interesantes sobre "Avanzado: E / S de archivos en segundo plano". Algunas de las opciones involucran el uso de un InputStream también. Apple también tiene algunas referencias más antiguas para leer y escribir archivos de forma asíncrona . Estoy publicando esta pregunta en anticipación de las alternativas Swift.

Ejemplo de una respuesta apropiada

Aquí hay un ejemplo de una respuesta apropiada que podría satisfacer este tipo de pregunta. (Tomado para la Guía de programación de secuencias, Escritura en flujos de salida )

El uso de una instancia NSOutputStream para escribir en una secuencia de salida requiere varios pasos:

  1. Cree e inicialice una instancia de NSOutputStream con un repositorio para los datos escritos. También establecer un delegado.
  2. Programe el objeto de secuencia en un bucle de ejecución y abra la secuencia.
  3. Manejar los eventos que el objeto de flujo informa a su delegado.
  4. Si el objeto de flujo ha escrito datos en la memoria, obtenga los datos solicitando la propiedad NSStreamDataWrittenToMemoryStreamKey.
  5. Cuando no haya más datos para escribir, elimine el objeto de flujo.

Estoy buscando el algoritmo más competente que se aplique a la escritura de archivos extremadamente grandes en iOS utilizando Swift, API o, posiblemente, incluso C / ObjC sería suficiente. Puedo transponer el algoritmo en construcciones compatibles Swift apropiadas.

Nota Bene

Entiendo el error informativo a continuación. Se incluye para la integridad. Esta pregunta es si hay o no un mejor algoritmo para escribir archivos grandes en el disco con una secuencia de dependencia garantizada (por ejemplo, dependencias de NSOperation). Si hay, proporcione suficiente información (descripción / muestra para que pueda reconstruir el código pertinente de Swift 2.0 compatible). Por favor avise si me falta información que ayude a responder la pregunta.

Nota sobre la extensión.

He agregado un controlador de finalización a la base writeToURL para garantizar que no se produzcan recursos compartidos involuntarios. Mis tareas dependientes que usan el archivo nunca deben enfrentar una condición de carrera.

extension NSData { func writeToURL(named:String, completion: (result: Bool, url:NSURL?) -> Void) { let filePath = NSTemporaryDirectory() + named //var success:Bool = false let tmpURL = NSURL( fileURLWithPath: filePath ) weak var weakSelf = self dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { //write to URL atomically if weakSelf!.writeToURL(tmpURL, atomically: true) { if NSFileManager.defaultManager().fileExistsAtPath( filePath ) { completion(result: true, url:tmpURL) } else { completion (result: false, url:tmpURL) } } }) } }

Este método se utiliza para procesar los datos de objetos personalizados desde un controlador usando:

var items = [AnyObject]() if let video = myCustomClass.data { //video is of type NSData video.writeToURL("shared.mp4", completion: { (result, url) -> Void in if result { items.append(url!) if items.count > 0 { let sharedActivityView = UIActivityViewController(activityItems: items, applicationActivities: nil) self.presentViewController(sharedActivityView, animated: true) { () -> Void in //finished } } } }) }

Conclusión

Los Apple Docs sobre Core Data Performance proporcionan algunos buenos consejos para lidiar con la presión de la memoria y administrar los BLOB. Este es realmente un gran artículo con muchas pistas sobre el comportamiento y cómo moderar el problema de los archivos grandes dentro de su aplicación. Ahora, aunque es específico de Datos centrales y no de archivos, la advertencia sobre escritura atómica me dice que debo implementar métodos que escriban de forma atómica con gran cuidado.

Con archivos grandes, la única forma segura de administrar la escritura parece ser agregar un controlador de finalización (al método de escritura) y mostrar una vista de actividad en el subproceso principal. El hecho de que uno haga eso con una transmisión o modificando una API existente para agregar la lógica de finalización depende del lector. He hecho ambas cosas en el pasado y estoy en medio de pruebas para obtener el mejor rendimiento.

Hasta entonces, estoy cambiando la solución para eliminar todas las propiedades de datos binarios de Core Data y reemplazándolas con cadenas para mantener las URL de los activos en el disco. También estoy aprovechando la funcionalidad incorporada de Assets Library y PHAsset para capturar y almacenar todas las URL de activos relacionados. Cuando o si necesito copiar algún activo, usaré métodos API estándar (métodos de exportación en PHAsset / Asset Library) con controladores de finalización para notificar al usuario sobre el estado finalizado en el hilo principal.

(Fragmentos realmente útiles del artículo de Core Data Performance)

Reducción de sobrecarga de memoria

A veces es el caso que desea utilizar objetos gestionados de forma temporal, por ejemplo, para calcular un valor promedio para un atributo particular. Esto hace que tu gráfico de objetos, y el consumo de memoria, crezcan. Puede reducir la sobrecarga de memoria reordenando objetos administrados individuales que ya no necesita, o puede restablecer un contexto de objeto administrado para borrar un gráfico de objeto completo. También puede utilizar patrones que se aplican a la programación de Cocoa en general.

Puede volver a fallar un objeto administrado individual usando el método refreshObject: mergeChanges: de NSManagedObjectContext. Esto tiene el efecto de borrar sus valores de propiedad en memoria, reduciendo así su sobrecarga de memoria. (Tenga en cuenta que esto no es lo mismo que establecer los valores de propiedad en cero; los valores se recuperarán a petición si se desencadena el fallo; consulte Faulting and Uniquing).

Cuando crea una solicitud de recuperación, puede establecer includesPropertyValues ​​en NO> para reducir la sobrecarga de memoria al evitar la creación de objetos para representar los valores de propiedad. Sin embargo, normalmente solo debe hacerlo si está seguro de que no necesitará los datos de propiedad reales o si ya tiene la información en el caché de filas, de lo contrario, incurrirá en varios viajes al almacén persistente.

Puede usar el método de restablecimiento de NSManagedObjectContext para eliminar todos los objetos administrados asociados con un contexto y "comenzar de nuevo" como si lo hubiera creado. Tenga en cuenta que cualquier objeto gestionado asociado con ese contexto será invalidado, por lo que deberá descartar cualquier referencia y volver a obtener cualquier objeto asociado con ese contexto en el que aún esté interesado. Si recorre una gran cantidad de objetos, es posible que deba usar bloques de agrupación de autorelease locales para asegurarse de que los objetos temporales se desasignen lo más pronto posible.

Si no tiene la intención de usar la funcionalidad de deshacer de Core Data, puede reducir los requisitos de recursos de su aplicación configurando el administrador de deshacer del contexto en nulo. Esto puede ser especialmente beneficioso para subprocesos de trabajo en segundo plano, así como para grandes operaciones de importación o por lotes.

Finalmente, Core Data no mantiene por defecto referencias fuertes a objetos gestionados (a menos que tengan cambios no guardados). Si tiene muchos objetos en la memoria, debe determinar las referencias propietarias. Los objetos gestionados mantienen fuertes referencias entre sí a través de relaciones, que pueden crear fácilmente ciclos de referencia fuertes. Puede interrumpir los ciclos volviendo a generar fallas en los objetos (nuevamente, utilizando el método refreshObject: mergeChanges: de NSManagedObjectContext).

Objetos de datos grandes (BLOBs)

Si su aplicación utiliza BLOB grandes ("Objetos grandes binarios" como datos de imagen y sonido), debe tener cuidado para minimizar los gastos generales. La definición exacta de "pequeño", "modesto" y "grande" es fluida y depende del uso de la aplicación. Una regla general es que los objetos del orden de kilobytes en tamaño tienen un tamaño "modesto" y los del orden de megabytes en tamaño "grande". Algunos desarrolladores han logrado un buen rendimiento con 10MB BLOB en una base de datos. Por otro lado, si una aplicación tiene millones de filas en una tabla, incluso 128 bytes pueden ser un CLOB (objeto grande de tamaño) de tamaño "modesto" que debe normalizarse en una tabla separada.

En general, si necesita almacenar BLOBs en un almacén persistente, debe usar un almacén SQLite. Los almacenes XML y binarios requieren que todo el gráfico de objetos resida en la memoria, y las escrituras de los almacenes son atómicas (ver Características persistentes de los almacenes), lo que significa que no tratan de manera eficiente los objetos de datos grandes. SQLite puede escalar para manejar bases de datos extremadamente grandes. Si se usa correctamente, SQLite proporciona un buen rendimiento para bases de datos de hasta 100 GB, y una sola fila puede contener hasta 1 GB (aunque, por supuesto, leer 1 GB de datos en la memoria es una operación costosa, sin importar cuán eficiente sea el repositorio).

Un BLOB a menudo representa un atributo de una entidad; por ejemplo, una fotografía puede ser un atributo de una entidad Empleado. Para BLOB (y CLOB) de tamaño pequeño a modesto, debe crear una entidad separada para los datos y crear una relación de uno a uno en lugar del atributo. Por ejemplo, puede crear entidades de Empleado y Fotografía con una relación de uno a uno entre ellas, donde la relación de Empleado a Fotografía reemplaza el atributo de fotografía del Empleado. Este patrón maximiza los beneficios de la falla de objetos (vea Faulting y Uniquing). Cualquier fotografía dada solo se recupera si es realmente necesaria (si la relación está atravesada).

Sin embargo, es mejor si puede almacenar BLOB como recursos en el sistema de archivos y mantener enlaces (como URL o rutas) a esos recursos. A continuación, puede cargar un BLOB cuando sea necesario.

Nota:

He movido la lógica a continuación al controlador de finalización (ver el código anterior) y ya no veo ningún error. Como se mencionó anteriormente, esta pregunta se refiere a si existe o no una forma más eficaz de procesar archivos grandes en iOS usando Swift.

Al intentar procesar la matriz de elementos resultante para pasar a un UIActvityViewController, utilizando la siguiente lógica:

si items.count> 0 {
let sharedActivityView = UIActivityViewController (activityItems: items, applicationActivities: nil) self.presentViewController (sharedActivityView, animated: true) {() -> Void in // finish}}

Estoy viendo el siguiente error: Error de comunicación: {count = 1, contents = "XPCErrorDescription" => {length = 22, contents = "Connection interrumpted"}}> (tenga en cuenta que estoy buscando un mejor diseño, no un responder a este mensaje de error)


Solución actual

No tengo dudas de que voy a refinar esto un poco más, pero el tema es lo suficientemente complejo como para justificar una respuesta propia por separado. Decidí seguir algunos consejos de las otras respuestas y aprovechar las subclases de NSStream. Esta solución se basa en una sample Obj-C ( NSInputStream inputStreamWithURL ejemplo ios , 2013, 12 de mayo) publicada en el blog SampleCodeBank .

La documentación de Apple señala que con una subclase NSStream NO tiene que cargar todos los datos en la memoria a la vez . Esa es la clave para poder administrar archivos multimedia de cualquier tamaño (sin exceder el espacio disponible en el disco o RAM).

NSStream es una clase abstracta para objetos que representan flujos. Su interfaz es común a todas las clases de flujo de Cocoa, incluidas sus subclases concretas NSInputStream y NSOutputStream.

Los objetos NSStream proporcionan una manera fácil de leer y escribir datos en y desde una variedad de medios de forma independiente del dispositivo. Puede crear objetos de flujo para los datos ubicados en la memoria, en un archivo o en una red (usando sockets), y puede usar objetos de flujo sin cargar todos los datos en la memoria a la vez.

Guía de programación del sistema de archivos

El artículo de Procesamiento de un archivo completo de Linearly Streams de Apple en el FSPG también proporcionó la idea de que NSInputStream y NSOutputStream deberían ser intrínsecamente seguros para subprocesos.

Más refinamientos

Este objeto no utiliza métodos de delegación de flujo. Mucho espacio para otros refinamientos también, pero este es el enfoque básico que tomaré. El enfoque principal en el iPhone es habilitar la gestión de archivos grandes al tiempo que restringe la memoria a través de un búfer ( TBD - Aproveche el búfer en memoria de outputStream ). Para ser claros, Apple menciona que sus funciones de conveniencia que writeToURL son solo para tamaños de archivo más pequeños (pero me pregunto por qué no se ocupan de los archivos más grandes: estos no son casos de borde, nota), la pregunta se archivará como un error ).

Conclusión

Tendré que probar más la integración en un subproceso en segundo plano, ya que no quiero interferir con ninguna NSStream interna de NSStream . Tengo algunos otros objetos que usan ideas similares para administrar archivos de datos extremadamente grandes a través del cable. El mejor método es mantener el tamaño de los archivos lo más pequeño posible en iOS para conservar la memoria y evitar bloqueos de aplicaciones. Las API se crean teniendo en cuenta estas restricciones (por lo que intentar video ilimitado no es una buena idea), por lo que tendré que adaptar las expectativas en general.

( Gist Source , cheque gist para los últimos cambios)

import Foundation import Darwin.Mach.mach_time class MNGStreamReaderWriter:NSObject { var copyOutput:NSOutputStream? var fileInput:NSInputStream? var outputStream:NSOutputStream? = NSOutputStream(toMemory: ()) var urlInput:NSURL? convenience init(srcURL:NSURL, targetURL:NSURL) { self.init() self.fileInput = NSInputStream(URL: srcURL) self.copyOutput = NSOutputStream(URL: targetURL, append: false) self.urlInput = srcURL } func copyFileURLToURL(destURL:NSURL, withProgressBlock block: (fileSize:Double,percent:Double,estimatedTimeRemaining:Double) -> ()){ guard let copyOutput = self.copyOutput, let fileInput = self.fileInput, let urlInput = self.urlInput else { return } let fileSize = sizeOfInputFile(urlInput) let bufferSize = 4096 let buffer = UnsafeMutablePointer<UInt8>.alloc(bufferSize) var bytesToWrite = 0 var bytesWritten = 0 var counter = 0 var copySize = 0 fileInput.open() copyOutput.open() //start time let time0 = mach_absolute_time() while fileInput.hasBytesAvailable { repeat { bytesToWrite = fileInput.read(buffer, maxLength: bufferSize) bytesWritten = copyOutput.write(buffer, maxLength: bufferSize) //check for errors if bytesToWrite < 0 { print(fileInput.streamStatus.rawValue) } if bytesWritten == -1 { print(copyOutput.streamStatus.rawValue) } //move read pointer to next section bytesToWrite -= bytesWritten copySize += bytesWritten if bytesToWrite > 0 { //move block of memory memmove(buffer, buffer + bytesWritten, bytesToWrite) } } while bytesToWrite > 0 if fileSize != nil && (++counter % 10 == 0) { //passback a progress tuple let percent = Double(copySize/fileSize!) let time1 = mach_absolute_time() let elapsed = Double (time1 - time0)/Double(NSEC_PER_SEC) let estTimeLeft = ((1 - percent) / percent) * elapsed block(fileSize: Double(copySize), percent: percent, estimatedTimeRemaining: estTimeLeft) } } //send final progress tuple block(fileSize: Double(copySize), percent: 1, estimatedTimeRemaining: 0) //close streams if fileInput.streamStatus == .AtEnd { fileInput.close() } if copyOutput.streamStatus != .Writing && copyOutput.streamStatus != .Error { copyOutput.close() } } func sizeOfInputFile(src:NSURL) -> Int? { do { let fileSize = try NSFileManager.defaultManager().attributesOfItemAtPath(src.path!) return fileSize["fileSize"] as? Int } catch let inputFileError as NSError { print(inputFileError.localizedDescription,inputFileError.localizedRecoverySuggestion) } return nil } }

Delegación

Aquí hay un objeto similar que reescribí de un artículo sobre E / S de archivos avanzada en segundo plano , Eidhof, C., ObjC.io ). Con solo unos cuantos ajustes, esto podría hacerse para emular el comportamiento anterior. Simplemente redirija los datos a un NSOutputStream en el método NSOutputStream .

( Fuente de la esencia: compruebe la esencia de los últimos cambios)

import Foundation class MNGStreamReader: NSObject, NSStreamDelegate { var callback: ((lineNumber: UInt , stringValue: String) -> ())? var completion: ((Int) -> Void)? var fileURL:NSURL? var inputData:NSData? var inputStream: NSInputStream? var lineNumber:UInt = 0 var queue:NSOperationQueue? var remainder:NSMutableData? var delimiter:NSData? //var reader:NSInputStreamReader? func enumerateLinesWithBlock(block: (UInt, String)->() , completionHandler completion:(numberOfLines:Int) -> Void ) { if self.queue == nil { self.queue = NSOperationQueue() self.queue!.maxConcurrentOperationCount = 1 } assert(self.queue!.maxConcurrentOperationCount == 1, "Queue can''t be concurrent.") assert(self.inputStream == nil, "Cannot process multiple input streams in parallel") self.callback = block self.completion = completion if self.fileURL != nil { self.inputStream = NSInputStream(URL: self.fileURL!) } else if self.inputData != nil { self.inputStream = NSInputStream(data: self.inputData!) } self.inputStream!.delegate = self self.inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) self.inputStream!.open() } convenience init? (withData inbound:NSData) { self.init() self.inputData = inbound self.delimiter = "/n".dataUsingEncoding(NSUTF8StringEncoding) } convenience init? (withFileAtURL fileURL: NSURL) { guard !fileURL.fileURL else { return nil } self.init() self.fileURL = fileURL self.delimiter = "/n".dataUsingEncoding(NSUTF8StringEncoding) } @objc func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent){ switch eventCode { case NSStreamEvent.OpenCompleted: fallthrough case NSStreamEvent.EndEncountered: self.emitLineWithData(self.remainder!) self.remainder = nil self.inputStream!.close() self.inputStream = nil self.queue!.addOperationWithBlock({ () -> Void in self.completion!(Int(self.lineNumber) + 1) }) break case NSStreamEvent.ErrorOccurred: NSLog("error") break case NSStreamEvent.HasSpaceAvailable: NSLog("HasSpaceAvailable") break case NSStreamEvent.HasBytesAvailable: NSLog("HasBytesAvaible") if let buffer = NSMutableData(capacity: 4096) { let length = self.inputStream!.read(UnsafeMutablePointer<UInt8>(buffer.mutableBytes), maxLength: buffer.length) if 0 < length { buffer.length = length self.queue!.addOperationWithBlock({ [weak self] () -> Void in self!.processDataChunk(buffer) }) } } break default: break } } func processDataChunk(buffer: NSMutableData) { if self.remainder != nil { self.remainder!.appendData(buffer) } else { self.remainder = buffer } self.remainder!.mng_enumerateComponentsSeparatedBy(self.delimiter!, block: {( component: NSData, last: Bool) in if !last { self.emitLineWithData(component) } else { if 0 < component.length { self.remainder = (component.mutableCopy() as! NSMutableData) } else { self.remainder = nil } } }) } func emitLineWithData(data: NSData) { let lineNumber = self.lineNumber self.lineNumber = lineNumber + 1 if 0 < data.length { if let line = NSString(data: data, encoding: NSUTF8StringEncoding) { callback!(lineNumber: lineNumber, stringValue: line as String) } } } }


Debería considerar el uso de NSStream (NSOutputStream/NSInputStream) . Si va a elegir este enfoque, tenga en cuenta que el bucle de ejecución del subproceso en segundo plano deberá iniciarse (ejecutarse) explícitamente.

NSOutputStream tiene un método llamado outputStreamToFileAtPath:append: que es lo que podría estar buscando.

Pregunta similar:

Escribir una cadena a un NSOutputStream en Swift


El rendimiento depende de si los datos encajan o no en la memoria RAM. Si lo hace, entonces debería usar NSData writeToURL con la función atomically activada, que es lo que está haciendo.

Las notas de Apple acerca de que esto es peligroso cuando "escribir en un directorio público" son completamente irrelevantes en iOS porque no hay directorios públicos. Esa sección solo se aplica a OS X. Y, francamente, tampoco es realmente importante allí.

Por lo tanto, el código que ha escrito es lo más eficiente posible siempre que el video se ajuste a la RAM (aproximadamente 100 MB sería un límite seguro).

Para los archivos que no caben en la RAM, debe usar una secuencia o su aplicación se bloqueará mientras mantiene el video en la memoria. Para descargar un video grande de un servidor y escribirlo en el disco, debe usar NSURLSessionDownloadTask .

En general, la transmisión (incluyendo NSURLSessionDownloadTask ) será órdenes de magnitud más lentas que NSData.writeToURL() . Así que no uses un stream a menos que lo necesites. Todas las operaciones en NSData son extremadamente rápidas, es perfectamente capaz de manejar archivos que tienen varios terabytes de tamaño con un excelente rendimiento en OS X (iOS, obviamente, no puede tener archivos tan grandes, pero es la misma clase con el mismo rendimiento).

Hay algunos problemas en su código.

Esto está mal:

let filePath = NSTemporaryDirectory() + named

En su lugar siempre hacer:

let filePath = NSTemporaryDirectory().stringByAppendingPathComponent(named)

Pero eso tampoco es lo ideal, debes evitar el uso de caminos (son buggy y lentos). En su lugar, use una URL como esta:

let tmpDir = NSURL(fileURLWithPath: NSTemporaryDirectory()) as NSURL! let fileURL = tmpDir.URLByAppendingPathComponent(named)

Además, estás usando una ruta para verificar si el archivo existe ... no hagas esto:

if NSFileManager.defaultManager().fileExistsAtPath( filePath ) {

En su lugar, use NSURL para verificar si existe:

if fileURL.checkResourceIsReachableAndReturnError(nil) {