8

I have a strange problem, I am trying to build an action extension that will scan barcode from the image provided. Here is the code.

override func viewDidLoad() {
    super.viewDidLoad()

    // Get the item[s] we're handling from the extension context.

    // For example, look for an image and place it into an image view.
    // Replace this with something appropriate for the type[s] your extension supports.
    var imageFound = false
    for item: AnyObject in self.extensionContext!.inputItems {
        let inputItem = item as NSExtensionItem
        for provider: AnyObject in inputItem.attachments! {
            let itemProvider = provider as NSItemProvider
            if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeImage as NSString) {
                // This is an image. We'll load it, then place it in our image view.
                weak var weakImageView = self.imageView
                itemProvider.loadItemForTypeIdentifier(kUTTypeImage as NSString, options: nil, completionHandler: { (image, error) in
                    if image != nil {
                        dispatch_async(dispatch_get_main_queue(),{
                            if let imageView = weakImageView {
                                var imageToSet: UIImage? = image as? UIImage
                                imageView.image = image as? UIImage
                            }

                            self.imageToScan = self.imageView.image
                            self.scanFromImage(self.imageToScan!)
                        })
                    }
                })

                imageFound = true
                break
            }
        }

        if (imageFound) {
            // We only handle one image, so stop looking for more.
            break
        }
    }
}

Now, whenever I try to get UIImage I always get nil, whereas in the image I can see an image is received. But when I try to get UIImage from that image it always returns nil. Here is the screen shot from debugging that might help

imageToSet Console The complete shot

Update: Here is the description of the image that is received as :

Printing description of image: (NSSecureCoding!) image = (instance_type = Builtin.RawPointer = 0x15d674c0 -> 0x32b6be5c (void *)0x32b6be70: NSURL)

I have created the same extension using objective C and it works, but not in swift. Here is objective C Code:

- (void)viewDidLoad {
[super viewDidLoad];

// Get the item[s] we're handling from the extension context.

// For example, look for an image and place it into an image view.
// Replace this with something appropriate for the type[s] your extension supports.
BOOL imageFound = NO;
for (NSExtensionItem *item in self.extensionContext.inputItems) {
    for (NSItemProvider *itemProvider in item.attachments) {
        if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) {
            // This is an image. We'll load it, then place it in our image view.
            __weak UIImageView *imageView = self.imageView;
            [itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(UIImage *image, NSError *error) {
                if(image) {
                     dispatch_async(dispatch_get_main_queue(), ^{
                        [imageView setImage:image];
                        imageToScan = image;
                        [self scanImage:imageToScan];
                    });
                }
            }];

            imageFound = YES;
            break;
        }
    }

    if (imageFound) {
        // We only handle one image, so stop looking for more.
        break;
    }
}

}

I tries to search a lot on Google but found nothing. I even tried a changed code but doe not work, here is the changed code:

                    itemProvider.loadItemForTypeIdentifier(kUTTypeImage as NSString, options: nil, completionHandler: { (image, error) in
                    if image != nil {
                        NSOperationQueue.mainQueue().addOperationWithBlock {
                            if let imageView = weakImageView {
                                var imageToSet: UIImage? = image as? UIImage
                                imageView.image = image as? UIImage
                            }

                            self.imageToScan = self.imageView.image
                            self.scanFromImage(self.imageToScan)
                        }
                    }
                })

Update: I have noticed one thing; if I create a new project and add an action extension to it, the same code is auto generated except few line that I added in the block. In that also without even changing a single line of auto generated code, the imageView.image is nil. Is this a bug in swift?? Or some bug with my Xcode app.

Andrew
  • 15,357
  • 6
  • 66
  • 101
Sharon Nathaniel
  • 1,467
  • 20
  • 28
  • Why on earth are you using an asynchronous dispatch? – Atomix Sep 17 '14 at 10:08
  • 1
    There might be many images that can we received, right now I am only handling one image for debugging so I used break when image is found. So if the extension receives more than one image, we can scan those images asynchronously. – Sharon Nathaniel Sep 17 '14 at 11:12
  • Are you aware, that the closure you are dispatching in Swift is different from the block in ObjC? – Atomix Sep 17 '14 at 11:44
  • No Joe, I think I might have to study it more. But if you can explain what the difference and why this code is not working; that would be a bit of guidance. – Sharon Nathaniel Sep 17 '14 at 12:25
  • @JoJoe even if I change my code to use block, the imageToSet is still nil. I am updating the question with changed code. – Sharon Nathaniel Sep 17 '14 at 13:07
  • Sorry, I made a mistake. I thought the code you are dispatching was different, but it isn't. Closures and Blocks are essentially the same thing. Unfortunately I can't find anything in your code. You should try to set a breakpoint and step through your code and watch closely what happens to each variable and see where it behaves strange. – Atomix Sep 17 '14 at 13:45

3 Answers3

7

the thing is that image is not UIImage, it's NSURL.

Change code to this one:

imageView.image = UIImage(data: NSData(contentsOfURL: image as NSURL)!)!
d.lebedev
  • 2,305
  • 19
  • 27
  • I will give this a try and provide feedback. Thanks! – Sharon Nathaniel Nov 10 '14 at 05:34
  • I have a problem, where Objective-C lets you grab the image data directly from the completion block parameters, but where Swift just returns an NSSecureCoding of 296 bytes, which (naturally) results in nil when trying to create an image from it. Did you (Simon) have any luck with the URL approach above? I will check if it solves my problem and post my findings here. – Daniel Saidi Nov 12 '14 at 11:31
  • Yes it works, i struggled with this as well. This is the case for the photos app, i'm wondering if gives an url for a photo from my own app as that one can't be accessed by everybody. – Cristi Băluță Nov 25 '14 at 07:34
  • I can't believe how few people have seen this... It's a bug in the latest Xcode iOS actions template. – Nick Dec 29 '14 at 01:22
3

U need to do like this

    if let strongImageView = weakImageView {

                   if let imageURL = image as? NSURL{

                 strongImageView.image = UIImage(data:NSData(contentsOfURL: imageURL)!)

                    }else{

                      strongImageView.image = image as? UIImage
                    }

                }

For Clarification I added Full Code Please refer, It worked for me

override func viewDidLoad() {
super.viewDidLoad()


// Get the item[s] we're handling from the extension context.

// For example, look for an image and place it into an image view.
// Replace this with something appropriate for the type[s] your extension supports.
var imageFound = false
for item: AnyObject in self.extensionContext!.inputItems {
    let inputItem = item as! NSExtensionItem
    for provider: AnyObject in inputItem.attachments! {
        let itemProvider = provider as! NSItemProvider
        if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
            // This is an image. We'll load it, then place it in our image view.
            weak var weakImageView = self.imageView
            itemProvider.loadItemForTypeIdentifier(kUTTypeImage as String, options: nil, completionHandler: { (image, error) in
                NSOperationQueue.mainQueue().addOperationWithBlock {


                if let strongImageView = weakImageView {

                       if let imageURL = image as? NSURL{

                     strongImageView.image = UIImage(data:NSData(contentsOfURL: imageURL)!)

                        }else{

                          strongImageView.image = image as? UIImage
                        }

                    }


                }
            })

            imageFound = true
            break
        }
    }

    if (imageFound) {
        // We only handle one image, so stop looking for more.
        break
    }
}
}
Sanju
  • 1,148
  • 11
  • 26
0

Getting image as

<UIImage: 0x16ecb410>, {100, 100}

It cannot be casted as NSURL and getting nil in the following expression.

imageView.image = UIImage(data: NSData(contentsOfURL: image as NSURL)!)!
Kampai
  • 22,848
  • 21
  • 95
  • 95