      :param: frameDelay an delay in seconds between each frame
      :param: callback set a block that will get called when done, it'll return with data and error, both which can be nil
      */
-    public func generateGifFromImages(imagesArray imagesArray:[UIImage], repeatCount: Int = 0, frameDelay: NSTimeInterval, destinationURL: NSURL, callback:(data: NSData?, error: NSError?) -> ()) {
+    open func generateGifFromImages(imagesArray:[UIImage], repeatCount: Int = 0, frameDelay: TimeInterval, destinationURL: URL, callback:@escaping (_ data: Data?, _ error: NSError?) -> ()) {
         
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { () -> Void in
+        DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
             
-            if let imageDestination = CGImageDestinationCreateWithURL(destinationURL, kUTTypeGIF, imagesArray.count, nil) {
+            if let imageDestination = CGImageDestinationCreateWithURL(destinationURL as CFURL, kUTTypeGIF, imagesArray.count, nil) {
                 
-                let frameProperties:CFDictionaryRef = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: frameDelay]]
-                let gifProperties:CFDictionaryRef = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: repeatCount]]
+                let frameProperties:CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: frameDelay]] as CFDictionary
+                let gifProperties:CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: repeatCount]] as CFDictionary
                 
                 for image in imagesArray {
-                    CGImageDestinationAddImage(imageDestination, image.CGImage!, frameProperties)
+                    CGImageDestinationAddImage(imageDestination, image.cgImage!, frameProperties)
                 }
                 
                 CGImageDestinationSetProperties(imageDestination, gifProperties)

import AVFoundation


@objc public class GifGenerator: NSObject {
    
    var cmTimeArray:[NSValue] = []
    var framesArray:[UIImage] = []

     :param: frameDelay an delay in seconds between each frame
     :param: callback set a block that will get called when done, it'll return with data and error, both which can be nil
     */
    open func generateGifFromVideoURL(videoURL videoUrl:URL, repeatCount: Int = 0, framesInterval:Int, frameDelay: TimeInterval, destinationURL: URL, callback:@escaping (_ data: Data?, _ error: NSError?) -> ()) {
        
        self.generateFrames(videoUrl, framesInterval: framesInterval) { (images) -> () in
            if let images = images {
                self.generateGifFromImages(imagesArray: images, repeatCount: repeatCount, frameDelay: frameDelay, destinationURL: destinationURL, callback: { (data, error) -> () in
                    self.cmTimeArray = []
                    self.framesArray = []
                    callback(data, error)
                })
            }
        }
    }
    
    // MARK: THANKS TO: http://stackoverflow.com/questions/4001755/trying-to-understand-cmtime-and-cmtimemake
    fileprivate func generateFrames(_ url:URL, framesInterval:Int, callback:@escaping (_ images:[UIImage]?) -> ()) {
        
        DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
            self.generateCMTimesArrayOfFramesUsingAsset(framesInterval, asset: AVURLAsset(url: url))
            
            var i = 0
            let test:AVAssetImageGeneratorCompletionHandler = { (_, im:CGImage?, _, result:AVAssetImageGeneratorResult, error:Error?) in
                if(result == AVAssetImageGeneratorResult.succeeded) {
                    print("Succeed")
                    if let image = im {
                        self.framesArray.append(UIImage(cgImage: image))
                    }
                } else if (result == AVAssetImageGeneratorResult.failed) {
                    print("Failed with error")
                    callback(nil);
                } else if (result == AVAssetImageGeneratorResult.cancelled) {
                    print("Canceled")
                    callback(nil);
                }
                i+=1
                if(i == self.cmTimeArray.count) {
                    //Thumbnail generation completed
                    callback(self.framesArray)
                }
            }
            let generator = AVAssetImageGenerator(asset: AVAsset(url: url))
            generator.apertureMode = AVAssetImageGeneratorApertureModeCleanAperture;
            generator.appliesPreferredTrackTransform = true;
            generator.requestedTimeToleranceBefore = kCMTimeZero;
            generator.requestedTimeToleranceAfter = kCMTimeZero;
            generator.maximumSize = CGSize(width: 200, height: 200);
            
            generator.generateCGImagesAsynchronously(forTimes: self.cmTimeArray, completionHandler: test)
        }
    }
    
    fileprivate func generateCMTimesArrayOfAllFramesUsingAsset(_ asset:AVURLAsset) {
        if cmTimeArray.count > 0 {
            cmTimeArray.removeAll()
        }
        
        for t in 0..<asset.duration.value {
            let thumbTime = CMTimeMake(t, asset.duration.timescale)
            let value = NSValue(time: thumbTime)
            cmTimeArray.append(value)
        }
    }
    
    fileprivate func generateCMTimesArrayOfFramesUsingAsset(_ framesInterval:Int, asset:AVURLAsset) {
        
        let videoDuration = Int(ceilf((Float(Int(asset.duration.value)/Int(asset.duration.timescale)))))
        
