fatal error: unexpectedly found nil while unwrapping an Optional value (lldb) - xcode

I am new to code and I am not sure how to get rid of my optional values here. I read somewhere that this could be my problem. any help would be great!
I have been following this tutorial:https://www.youtube.com/watch?v=Qyy8pJd4IWU
#IBAction func dropPhoto(sender: AnyObject) {
presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [NSObject : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
let thumbnail = image.resizedImageWithContentMode(UIViewContentMode.ScaleAspectFit, bounds: CGSizeMake(400, 400), interpolationQuality: CGInterpolationQuality.High)
let imgData = UIImagePNGRepresentation(thumbnail)
let base64EncodedImage = imgData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
let uniqueReference = firebase?.childByAutoId()
uniqueReference!.setValue(base64EncodedImage)
let key = uniqueReference?.key
_ = mapView.userLocation.location
geofire!.setLocation(mapView.userLocation.location,forKey: key)
}

Whenever you see this error, look for "!"s
There are two lines here that contain a force-unwrap
geofire!.setLocation(mapView.userLocation.location,forKey: key)
and
uniqueReference!.setValue(base64EncodedImage)
you should be able to fix it by simply replacing the ! with a ? , e.g.
geofire?.setLocation(mapView.userLocation.location,forKey: key)
which will cause setLocation to be called only if geoFire is a real value, otherwise if you want to also handle the nil case, the swift way is:
if let geoFire = geoFire {
geoFire.setLocation(mapView.userLocation.location, forKey: key)
}
else{
*do something*
}

You could also add an assert or precondition checking firebase and geofire at the beginning of the function. Here is a third approach that will check those values and stop execution on your debug builds but otherwise simply return on release builds. That will make the later method calls into firebase and geofire safe.
You will still need to identify why one of your references is unexpectedly nil and handle that case. Maybe never call this image picker function in the first place or simply remove the assertFailure statements and let the function return silently without doing anything. Your choice.
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [NSObject : AnyObject]?) {
guard let firebase = firebase else {
assertionFailure("Missing Firebase reference")
return
}
guard let geofire = geofire else {
assertionFailure("Missing Geofire reference")
return
}
self.dismissViewControllerAnimated(true, completion: nil)
let thumbnail = image.resizedImageWithContentMode(UIViewContentMode.ScaleAspectFit, bounds: CGSizeMake(400, 400), interpolationQuality: CGInterpolationQuality.High)
let imgData = UIImagePNGRepresentation(thumbnail)
let base64EncodedImage = imgData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions())
uniqueReference = firebase.childByAutoId()
uniqueReference.setValue(base64EncodedImage)
geofire.setLocation(mapView.userLocation.location,forKey: uniqueReference.key)
}

Related

Is it possible to use async/await syntax for UIActivityViewController's completionWithItemsHandler property?

I have this function:
struct Downloader {
static func presentDownloader(
in viewController: UIViewController,
with urls: [URL],
_ completion: #escaping (Bool) -> Void
) {
DispatchQueue.main.async {
let activityViewController = UIActivityViewController(
activityItems: urls,
applicationActivities: nil
)
activityViewController.completionWithItemsHandler = { _, result, _, _ in
completion(result)
}
viewController.present(
activityViewController,
animated: true,
completion: nil
)
}
}
}
It simply creates a UIActivityViewController and passes the completionWithItemsHandler as a completion block into the static func.
I am now trying to get rid of all the #escaping/closures as much as I can in order to adopt the new async/await syntax, however I don't know if I can do something here with this.
I started adding an async to my function, and realized that Xcode shows completionWithItemsHandler with an async keyword, but I really have no idea if I can achieve what I want here.
Thank you for your help
Yes, you can do that but you need to write yourself little wrapper over it. So it will look something like that:
#MainActor
func askToShareFile(url: URL) async {
let avc = UIActivityViewController(activityItems: [url], applicationActivities: nil)
present(avc, animated: true)
return await withCheckedContinuation { continuation in
avc.completionWithItemsHandler = { activity, completed, returnedItems, activityError in
print("Activity completed: \(completed), selected action = \(activity), items: \(returnedItems) error: \(activityError)")
if completed {
continuation.resume()
} else {
if activity == nil {
// user cancelled share sheet by closing it
continuation.resume()
}
}
}
}
}
However, important note here that, from my experimentation as of today (iOS 15.5), I can see completion handler is NOT called properly when, on share sheet, user selects any app that handle our file by copying it (activityType = com.apple.UIKit.activity.RemoteOpenInApplication-ByCopy).
If you do some changes - be careful, as you might loose continuation here so please test it yourself too.

Xcode code completion suggests `printContent(_)` above `print(_)`

Using Xcode 13, typing any substring of print suggests printContent() first in the Xcode code completion list above the common Swift print() function(s).
printContent(_ sender: Any?)
Tells your app to print available content.
"Jump to Definition" displays the following declaration, new for iOS 15 in 2021:
public protocol UIResponderStandardEditActions : NSObjectProtocol {
// ...
#available(iOS 15.0, *)
optional func printContent(_ sender: Any?)
}
What is the printContent() function and how is it used?
Is it in any way a new better replacement for print(), justifying its prominent code completion location in Xcode?
If not, how can I go back to pre-Xcode 13 behavior and suggest the extremely common print() function first in the list?
If your app includes the UIApplicationSupportsPrintCommand key in its Info.plist file, people can print from your app using the keyboard shortcut Command-P, which calls printContent(_:). You can also set printContent(_:) as the action on other print-related controls such as a print button on a toolbar.
override func printContent(_ sender: Any?) {
let info = UIPrintInfo.printInfo()
info.outputType = .photo
info.orientation = .portrait
info.jobName = modelItem.title
let printInteractionController = UIPrintInteractionController()
printInteractionController.printInfo = info
printInteractionController.printingItem = modelItem.image
let completionHandler: UIPrintInteractionController.CompletionHandler = {
(controller: UIPrintInteractionController, completed: Bool, error: Error?) in
if let error = error {
Logger().error("Print failed due to an error: \(error.localizedDescription)")
}
}
if traitCollection.userInterfaceIdiom == .pad {
if let printButton = navigationItem.rightBarButtonItem {
printInteractionController.present(from: printButton, animated: true, completionHandler: completionHandler)
}
} else {
printInteractionController.present(animated: true, completionHandler: completionHandler)
}
}
Apple developer link

Updated from Swift 1 to Swift 2

I wrote this code in the previous version of Swift and Xcode and now when I updated xcode to 7.0.1 and updated swift to another version I got a lot of errors. This is something I can't solve and it would be so nice if you could help me. The fat text is the error. The part from >>> to <<< is the part that I need help with so the > and < is not in the real code.
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
>>> var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)! <<<
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as! GameScene
archiver.finishDecoding()
return scene
} else {
return nil
}
}
}
Here, I made your code nicer and safe. This code (in theory) cannot crash whatever happens:
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
guard let
path = NSBundle.mainBundle().pathForResource(file, ofType: "sks"),
sceneData = try? NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe) else {
return nil
}
let archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
guard let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as? GameScene else {
return nil
}
archiver.finishDecoding()
return scene
}
}
If you don't know how the new error handling and guard works, you should check online.
Also if you really want to be fancy, you can use the new defer:
extension SKNode {
class func unarchiveFromFile(file : String) -> SKNode? {
guard let
path = NSBundle.mainBundle().pathForResource(file, ofType: "sks"),
sceneData = try? NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe) else {
return nil
}
let archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
defer { archiver.finishDecoding() }
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
return archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as? GameScene
}
}
In swift 2.0 you have to manage errors thrown:
do {
var sceneData = try NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
} catch let error as NSError {
// manage error case
}
There is no longer an NSData.init(contentsOfFile:options:error:) constructor. Most Cocoa calls that returned an error this way are now throwing. The new way to do what you've done here is:
var sceneData = try! NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
Rather than getting back an optional and then force-unwrapping it, this uses a force-try to do the same thing. In both cases, this will crash if the read fails. Generally a normal try inside of a do/catch is preferred (just like you generally should not force-unwrap optionals).
You need to download the new version of the Swift language book. Enums that consist of bits that should be combined are now represented as sets, not as integer values. Everything is nicely explained in Apple's documentation.
Reading release notes is also very, very useful. At this point in time, Apple doesn't have the slightest scruples to modify the language in a way that breaks existing code. You need to keep up with it yourself.

Send email In-game using sprite kit in xcode 7 beta3?

I am making an iPad game in sprite kit using swift in xcode 7beta3 and I want the results of the game to be send to the users email after the game is completed. The user should press a button called send and redirect to where they can type in their email-address and send the message. But I have no idea how to make and send an email.
I have been searching all around the internet for an answer to this question, but all are older version answers. I hope you can help.
Thanks in advance
EDIT:
I have been searching some more and i found a solution (here: http://kellyegan.net/sending-files-using-swift/), but I still have a problem. In my GameViewController i have added:
override func viewDidLoad() {
super.viewDidLoad()
let scene = StartGameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
skView.presentScene(scene)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
internal func sendEmail() {
//Check to see the device can send email.
if( MFMailComposeViewController.canSendMail() ) {
print("Can send email.")
let mailComposer = MFMailComposeViewController()
mailComposer.mailComposeDelegate = self
//Set the subject and message of the email
mailComposer.setSubject("Have you heard a swift?")
mailComposer.setMessageBody("This is what they sound like.", isHTML: false)
if let filePath = NSBundle.mainBundle().pathForResource("Math", ofType: "txt") {
print("File path loaded.")
if let fileData = NSData(contentsOfFile: filePath) {
print("File data loaded.")
mailComposer.addAttachmentData(fileData, mimeType: "text/plain", fileName: "Math")
}
}
self.presentViewController(mailComposer, animated: true, completion: nil)
}
}
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
self.dismissViewControllerAnimated(true, completion: nil)
}
The sendMail() is called in one of my gameScenes when you press a button.
The problem is that I get an error when I press that button. It prints out
Can send email.
File path loaded.
File data loaded.
as it should, but then it gives an error:
Could not cast value of type 'UIView' (0x1964ea508) to 'SKView' (0x19624f560).
I think the problem is the self.presentViewController(), but I have no idea how to fix it.

AVPlayerItem videoComposition freeze IOS8

I am playing a AVMutableVideoComposition with AVPlayer and since IOS8 everything was perfectly fine.
But now the video start playing and after 4 or 5 seconds it stop ,like buffering or something like that, the sound keeps playing and when the video ends the AVPlayer loops and play it fine without stops.
I have no clue for fixing this issue.
Any help would be appreciate,
Thank you
I had a same issue, but I got the solution.
You should play after playerItem's status is changed to .ReadyToPlay.
I also answered here, that issue is similar to your issue.
Please see as below.
func startVideoPlayer() {
let playerItem = AVPlayerItem(asset: self.composition!)
playerItem.videoComposition = self.videoComposition!
let player = AVPlayer(playerItem: playerItem)
player.actionAtItemEnd = .None
videoPlayerLayer = AVPlayerLayer(player: player)
videoPlayerLayer!.frame = self.bounds
/* add playerItem's observer */
player.addObserver(self, forKeyPath: "player.currentItem.status", options: .New, context: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerItemDidReachEnd:", name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem);
self.layer.addSublayer(videoPlayerLayer!)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if keyPath != nil && keyPath! == "player.currentItem.status" {
if let newValue = change?[NSKeyValueChangeNewKey] {
if AVPlayerStatus(rawValue: newValue as! Int) == .ReadyToPlay {
playVideo() /* play after status is changed to .ReadyToPlay */
}
}
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
func playerItemDidReachEnd(notification: NSNotification) {
let playerItem = notification.object as! AVPlayerItem
playerItem.seekToTime(kCMTimeZero)
playVideo()
}
func playVideo() {
videoPlayerLayer?.player!.play()
}
Same here, don't know if it can be count like an answer but anyway, just use
[AVPlayer seekToTime:AVPlayer.currentItem.duration]; to make first loop by yourself and to avoid AVPlayer stop. That's only way that i found.
I was having the same problem and i solved it this way ..
Instead of applying videoComposition to AVPlayerItem directly .. i exported my video using AVAssetExportSession to apply videoComposition, which in turn gives me a Url with a video having all my videoComposition applied and now i can use this contentURL to play video on AVPlayer..
This works like a charm ..
Exporting video will take time but as now rendering video composition is not happening on the go, it will work smoothly..
Following code can be used to export video ..
AVAssetExportSession *export = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPreset1280x720];
export.videoComposition = videoComposition;
export.outputURL = [NSURL fileURLWithPath:[[NSTemporaryDirectory() stringByAppendingPathComponent:[NSUUID new].UUIDString] stringByAppendingPathExtension:#"MOV"]];
export.outputFileType = AVFileTypeQuickTimeMovie;
export.shouldOptimizeForNetworkUse = YES;
[export exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (export.status == AVAssetExportSessionStatusCompleted) {
completionHander(export.outputURL, nil);
} else {
completionHander(nil, export.error);
}
});
}];

Resources