"Can't unwrap Optional.None" and I can't figure out why - swift

This produces "fatal error: Can't unwrap Optional.None" and I don't seem to get why
var motionManager = CMMotionManager()
motionManager.accelerometerUpdateInterval = 0.2
motionManager.startAccelerometerUpdates()
var accelerationData = motionManager.accelerometerData
var accel = accelerationData.acceleration.x
If anyone can help me out, that would be great.

The issue is accelerationData is nil and you aren't checking for this. From the docs:
If no accelerometer data is available, the value of this property is nil.
You should check to make sure there is actually data before calling methods on it like this
if let accelerationData = motionManager.accelerometerData {
var accel = accelerationData.acceleration.x
}
That will ensure that if there is no data your app won't crash. Now to make sure you get some data.
You aren't getting any data because you're asking for data immediately after you initialize the core motion manager. You can show this by waiting a few seconds before checking. You can add NSThread.sleepForTimeInterval(3) right above the if let and run the project and it will enter the if let. Make sure you are using an actual device though, the simulator won't generate any motion data.

Related

Retrieving NSOrderedSet from Core Data and casting it to entity managedObjectSubclasss

Im making a Fitness app to learn Core data, and I have found that I need to let the user store every performed workout as a WorkoutLog item, and in there, there should be a series of ExerciseLogs which represents performances of that exercise (it contains each lift and also a reference to the actual exercise design).
Problem is that after a while i realize that i need to have these ordered, so that the next time i want to show the user their workout, the order that the exercisese were performed should be the same.
So I checked "ordered" in the top right of the image there, and now my code is in dire need of an update. I have tried to read as much as I could about working with NSOrderedSet and how to fetch them from core data and then manipulate them, but I havent really found much of use to me. (I have no experice in objective-c)
For example my code that used to be:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
guard let exerciseLogsToDelete = workoutLogToDelete.loggedExercises as? Set<ExerciseLog> else {
print("error unwrapping logged exercises in deleteWorkoutLog")
return
}
I get the error: .../DatabaseFacade.swift:84:77: Cast from 'NSOrderedSet?' to unrelated type 'Set' always fails
So what ive learned about sets and core data no longer seems applicable.
Im far from an expert in programming, but im very eager to learn how to get access to the loggedExercises instances.
TLDR; Is there a way to cast NSOrderedSet to something I can work with? How do we usually work with NSManagedSets from core data? Do we cast them to Arrays or MutableSets? I would very much appreciate an example or two on how to get started with retrieving and using these ordered sets!
Thanks
For anyone else wondering how to get started with orderedSets in core data:
After setting my the WorkoutLog.loggedExercises "to-many" relationship to be ordered, I managed to access them through the mutableOrderedSetValue function like this:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
let orderedExerciseLogs: NSMutableOrderedSet = workoutLogToDelete.mutableOrderedSetValue(forKey: "loggedExercises")
let exerciseLogsToDelete = orderedExerciseLogs.array
for exerciseLog in exerciseLogsToDelete {
guard let exerciseLog = exerciseLog as? ExerciseLog else {
return
}
Works great so far.
And to rearrange the NSOrderedSet I ended up doing something like this:
// Swap the order of the orderedSet
if let orderedExerciseLogs: NSOrderedSet = dataSourceWorkoutLog.loggedExercises {
var exerciseLogsAsArray = orderedExerciseLogs.array as! [ExerciseLog]
let temp = exerciseLogsAsArray[indexA]
exerciseLogsAsArray[indexA] = exerciseLogsAsArray[indexB]
exerciseLogsAsArray[indexB] = temp
let exerciseLogsAsOrderedeSet = NSOrderedSet(array: exerciseLogsAsArray)
dataSourceWorkoutLog.loggedExercises = exerciseLogsAsOrderedeSet
}

Cannot get value from Accelerometer, Gyro and DeviceMotion

I'm making an app to track your sleep by getting any movement of the iPhone. I searched everywhere and there are many ways you can get data from this but non worked. Here's my code.
func startManager() {
let manager = CMMotionManager()
manager.startAccelerometerUpdates()
let accelerometer = manager.accelerometerData
}
But manager.accelerometerData is nil. I also tried it with DeviceMotion, Gyro and UserAcceleration, non of them worked. I also tried this.
print(manager.isAccelerometerActive)
It printed false so I think this is the problem but I still cannot make it active.
I tried putting the let manager = CMMotionManager() to the top and it still won't work.
I tried putting manager.startAccelerometerUpdates() in the ViewDidLoad and still doesn't work.
I also tried another way which is this.
manager.startAccelerometerUpdates(to: OperationQueue.main) { (data : CMAccelerometerData, error : Error) in
print(data)
}
but it gave me an error saying that I have to add as! CMAccelerometerHandler but after I added that it tell me to add it again.
I want to get raw value from the Accelerometer as x, y and z
(I imported CoreMotion)
I'm doing this using Swift 3 on XCode 8.2.1 and testing it on iPhone 6s.
In Swift3, you should use optional parameters in the handler
motionManager.startAccelerometerUpdates(
to: OperationQueue.main ,
withHandler:{ (data : CMAccelerometerData?, error : Error?) in
print(data)
})

Using NSSpeechStatusNumberOfCharactersLeft to update a progress indicator for NSSpeechSynthesizer

I'm new to Swift and OS X programming. I'm trying to use a progress monitor to indicate the progress of my speech synthesizer speaking text.
let speechSynthesizer = NSSpeechSynthesizer()
speechSynthesizer.delegate = self;
speechSynthesizer.startSpeakingString(contents)
I would like to set
progressIndicator.maxValue = Double(NSSpeechStatusNumberOfCharactersLeft.characters.count)
and then periodically update the progressIndicator with NSSpeechStatusNumberOfCharactersLeft, which according to Apple's documentation should hit 0.
Every way I try to access this key, it returns the same inaccurate number, so I'm obviously not using it correctly. The only example I found was in Objective-C
NSNumber *n = [[self.speechSynth objectForProperty:NSSpeechStatusProperty error:NULL] objectForKey:NSSpeechStatusNumberOfCharactersLeft];
and I tried to translate that to Swift, but still, no dice.
let count = try speechSynthesizer.objectForProperty(NSSpeechStatusProperty).objectForKey(NSSpeechStatusNumberOfCharactersLeft)
I've also tried
speechSynthesizer.valueForKey(NSSpeechStatusNumberOfCharactersLeft))
speechSynthesizer.valueWithName(NSSpeechStatusNumberOfCharactersLeft, inPropertyWithKey: NSSpeechStatusProperty))
which throw runtime exceptions. Any thoughts? Thanks in advance!
Your second attempt is pretty close to correct:
let count = try speechSynthesizer.objectForProperty(NSSpeechStatusProperty).objectForKey(NSSpeechStatusNumberOfCharactersLeft)
Without an error message (and since I don't have a Swift 2 compiler handy) I'm guessing the reason that this fails is that the return value of objectForProperty isn't known to be an dictionary, so you can't look up values in it.
Here's my quick & dirty Swift 3 playground for testing this:
import PlaygroundSupport
import Cocoa
let synth = NSSpeechSynthesizer()
synth.startSpeaking("I'm not standing still, I am lying in wait")
// quick way to test for progress without setting up an app and delegate
PlaygroundPage.current.needsIndefiniteExecution = true
let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
let statusDict = try! synth.object(forProperty: NSSpeechStatusProperty) as! [String: Any]
print(statusDict[NSSpeechStatusNumberOfCharactersLeft])
}
(Obviously, you don't need all the timer stuff or playground business if you're running this in an app with a synthesizer delegate.)
The key bit (no pun intended) is to cast the return value of objectForProperty so that Swift knows it's a dictionary, then look up the number of characters in that dictionary.
Now, this code runs correctly, but it doesn't completely work for setting a progress bar — when the synthesizer finishes speaking, NSSpeechStatusNumberOfCharactersLeft is likely to be some nonzero value. (In that case, another status key, NSSpeechStatusOutputBusy will become false.) So your progress bar will get to not quite 100%, and you can use either the NSSpeechStatusOutputBusy key or the delegate didFinishSpeaking callback to take your bar the rest of the way, remove your progress UI, or whatever.

NSDictionary: error Unexpectedly found nil while unwrapping an Optional value

I was playing with an idea in the Playground. This idea NSDictionaries used as a way to "keep" data.
I started creating a variable called layer type [String:[String:String]].
So, an error occurred. I'm an hour trying to solve, and could not find the error reason happen. I am new to Swift.
var layers: [String:[String:String]]!
layers["key"] = ["layer":"layer"]
layers["key2"] = ["asd":"12312"]
print(layers)
Could someone help me? Or tell me how can I get the end result of my NSDictionaries?
You've declared the type of the variable layers but you haven't allocated storage for it yet.
Try this instead:
var layers = [String:[String:String]]()
If you insist on layers being an implicitly unwrapped optional, then you could initialize it this way:
var layers: [String:[String:String]]! = [:]
This would allow you to assign nil to it later, but that would be dangerous because it would crash if you try to use it when it is nil. That is the reason for your crash.
If you want it to be optional, you should declare it with ? so that it can be safely unwrapped:
var layers: [String:[String:String]]?
// Sometime later
layers = [:]
// use optional chaining to assign values, this safely does
// nothing if layers is nil
layers?["key"] = ["layer":"layer"]
layers?["key2"] = ["asd":"12312"]
// use optional binding to unwrap layers
if let unwrapped_layers = layers {
print(unwrapped_layers)
}
Try this in a Playground, and then try commenting out the layers = [:] part and you will see that it doesn't crash because all accesses to layers are done in a safe manner that properly handle the case when layers is nil.

Swift NSUserDefaults first time nil

Hi my app crashes the first time I run it. This is my code:
let State = save.stringForKey("StateSave")
let City = save.stringForKey("CitySave")
let Vehicle = save.stringForKey("ModelNumberSave")
let ExtensionPeriod = save.stringForKey("ExtensionPeriodChoosed")
let Location = "Location"
if ExtensionPeriod == nil {
let name = ""
var FieldChoosed: Void = save.setObject(name, forKey: "ExtensionPeriodChoosed")
save.synchronize()
}
save.synchronize()
var DetailNames = ["\(State!)","\(City!)","\(Location)","\(Vehicle!)","\(ExtensionPeriod!)"]
I get a nil on this line:
var DetailNames =
["(State!)","(City!)","(Location)","(Vehicle!)","(ExtensionPeriod!)"]
In fact ExtensionPeriod is nil. But I don't understand why... ExtensionPeriod is nil, but with the code I write, ExtensionPeriod will be like "" so it's not nil. Please help me.
stringForKey returns nil when there has not been a value saved already.
You need to give your values a default. The easiest way to do this is with the ?? operator, that replaces nil with another value:
let State = save.stringForKey("StateSave") ?? "-"
Some general advice: you need to stop using ! so much. Usually when something returns nil, it’s for a good reason – it might be nil. When you unwrap it with !, your program will crash (with not much helpful info as to why). Similarly, it’s usually a bad sign if you’re comparing values to nil.
Instead, take a look at this list of optional handling techniques for some alternatives.
Airspeed Velocity has a good solution for the proper way to accomplish what you want to do, but he did not really explain why what you did does not work, so I will address that aspect of this question.
if ExtensionPeriod == nil {
let name = ""
var FieldChoosed: Void = save.setObject(name, forKey: "ExtensionPeriodChoosed")
save.synchronize()
}
That block of code does not set ExtensionPeriod, thus ExtensionPeriod is still nil. All it does is set the value for the key "ExtensionPeriodChoosed" in the NSUserDefaults to no longer be nil. The local variable ExtensionPeriod, however, still has nil. ExtensionPeriod doesn't just magically point to the variable stored in NSUserDefaults, such that when you update NSUserDefaults, it automatically updates the variable. Instead, it copies the variable at the time that it is created.
Here is some sample code that demonstrates this:
NSUserDefaults.standardUserDefaults().setValue("string", forKey: "s")
NSUserDefaults.standardUserDefaults().synchronize()
var s = NSUserDefaults.standardUserDefaults().valueForKey("s")
NSUserDefaults.standardUserDefaults().setValue("string2", forKey: "s")
NSUserDefaults.standardUserDefaults().synchronize()
var y = NSUserDefaults.standardUserDefaults().valueForKey("s")
println(s)
println(y)
outputs:
"string"
"string2"
For your code to work, if you were to keep it the same structure (although you really shouldn't), you would need to change ExtensionPeriod to a var, and then in your if block, after you synchronize save, you would have to reassign ExtensionPeriod to save.valueForKey("ExtensionPeriodChoosed").
One way to make sure that your app's defaults are set is to use NSUserDefault's registerDefaults(_: [NSObject : AnyObject]) function. In Objective-C, I often put in the + (void)initialize class method, but overriding the init() of the application delegate should work just as well.