How to safely force unwrap time in dictionary - swift

I have an issue. I have a dictionary type [String: Any]
my code that works is
dict["start"] = "\(start.hour!):\(start.minute!)"
if let end = end {
dict["end"] = "\(end.hour!):\(end.minute!)"
}
But as I use swiftlint it throws me an error for force unwrapping. Value must be saved so if let is not good here :)

that is mostly a semantic issue, but you could do something like this:
if let startHour = start.hour,
let startMinute = start.minute {
dict["start"] = "\(startHour):\(startMinute)"
if let end = end,
let endHour = end.hour,
let endMinute = end.minute {
dict["end"] = "\(endHour):\(endMinute)"
}
}
...or something similar – as there are various ways in Swift to safely unwrap an optional.

You can try the following demo code. I hope it will help you.
import Foundation
let calendar = Calendar.current
let dateComponents = DateComponents(hour: 12, minute: 20, second: 55)
func getHoursMinutesFrom(time: DateComponents) -> (hour: Int,minute: Int) {
switch (time.hour, time.minute) {
case let (.some(hour), .some(minutes)):
return (hour,minutes)
default:
return (0,0)
}
}
print(getHoursMinutesFrom(time: dateComponents))

Related

archivedData(withRootObject:)' was deprecated in iOS 12.0 - Swift 5 and Flutter

I have a project which was written (iOS part) with Swift 4. I update it to 5 but now I have this warnings. I've tried to fix it as it's suggest but I get different error each time. Could you help me with it?
Code
func saveData(data: BloodData) {
if let defaults = getUserDefaults() {
BloodData.registerClassName()
let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: data)
defaults.set(encodedData, forKey: dataKey)
defaults.synchronize()
}
}
Warning
archivedData(withRootObject:)' was deprecated in iOS 12.0: Use +archivedDataWithRootObject:requiringSecureCoding:error: instead
Code
func getData() -> BloodData? {
BloodData.registerClassName()
if let defaults = getUserDefaults(), let data = defaults.data(forKey: dataKey), let decodedData = NSKeyedUnarchiver.unarchiveObject(with: data) as? BloodData {
return computeData(data: decodedData)
}
return nil
}
Warning
'unarchiveObject(with:)' was deprecated in iOS 12.0: Use +unarchivedObjectOfClass:fromData:error: instead
Here's a computeData func:
private func computeData(data: BloodData) -> BloodData? {
let today = Date()
let calendar = Calendar.current
let date1 = calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: data.readingDate))!
let date2 = calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: today))!
let components = calendar.dateComponents([.day], from: date1, to: date2)
let dayDiff = components.day ?? 0
let newBloodData = BloodData(
whiteCells: data.whiteCells - dayDiff,
fullBlood: data.fullBlood - dayDiff,
platelet: data.platelet - dayDiff,
redCells1: data.redCells1 - dayDiff,
redCells2: data.redCells2 - dayDiff,
bloodPlasma: data.bloodPlasma - dayDiff,
readingDate: date2
)
return newBloodData
}
When coding in Swift most of the time the errors are not very helpful.
The first one is pretty self explanatory, except that the suggested method might throw an error. You can remove the throw keyword and ignore the error with try? if you would like to but it is better to catch and handle the errors:
func save(blood: BloodData, forKey defaultName: String = "BloodDataKey") { // or forKey defaultName: String = dataKey
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: blood, requiringSecureCoding: false) else { return }
UserDefaults.standard.set(data, forKey: defaultName)
}
The second error doesn't help at all. You can use NSKeyedUnarchiver's method unarchiveTopLevelObjectWithData and it throws as well:
func getBlood(forKey defaultName: String = "BloodDataKey") -> BloodData? { // or forKey defaultName: String = dataKey
guard let data = UserDefaults.standard.data(forKey: defaultName) else { return nil }
guard let bloodData = (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)) as? BloodData else { return nil }
return computeData(data: bloodData)
}

Why is this constant from a Dictionary not unwrapped?

I'm using some base swift functionality, the fact that you can unwrapped Optional values with a if. I am working on swift 4 and Xcode 10.3
let xyz: String? = nil
if let unwrappedValue = xyz {
print("This is unwrapped")
} else {
print("This is not unwrapped") // Program goes there
}
I have made a dictionary with an Optional value as an element, and when I try to unwrap it, it doesn't unwrap the variable, but keeps the Optional value.
var referenceDatesIdentifiers: [String: Date?] =
["ReferenceDateIdentifier": nil]
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate!) // ReferenceDate is Optional and not unwrapped
}
Why isn't referenceDate unwrapped ?
I tried to export the elem of the Dictionary in a constant, but same problem, it's not unwrapped
let refDateOpt = referenceDatesIdentifiers["ReferenceDateIdentifier"]
if let referenceDate = refDateOpt {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate!) // Reference date is also Optional and not unwrapped
}
What is happening ?
You should required again optionally unwrap date with type casting will fix your issue. Update code as follow:
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] as? Date {
print(referenceDate)
}
The problem here is that it doesn't makes any sense to create a dictionary with a nil value. So instead of checking if the key exists before adding a value, I just add the value and check the value with a if let
In the end, this was the answer
var referenceDatesIdentifiers: [String : Date] = [:]
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate)
}
You could use below way. It will work perfectly.Update code as follow:
var referenceDatesIdentifiers: [String: Date?] =
["ReferenceDateIdentifier": nil]
if let refDateOpt = referenceDatesIdentifiers["ReferenceDateIdentifier"] as? Date {
print(refDateOpt)
}
or
if let refDateOpt = referenceDatesIdentifiers["ReferenceDateIdentifier"]{
if let referenceDate = refDateOpt {
print(referenceDate)
}
}

How to avoid this Force Cast

I think that a force cast I have in my app is causing it to crash, (userDefaults.value(forKey: "timeDiffSecondsDefault") as! Int?)...but I really don't know how to avoid it. Any guidance is greatly appreciated!
func getProductionTime(store: Bool = false) {
let userDefaults = UserDefaults.standard
let productionTimeFormatter = DateFormatter()
productionTimeFormatter.timeZone = TimeZone(abbreviation: defaultTimeZone)
productionTimeFormatter.dateFormat = defaultTimeFormat
if let defaultTimeDiffSeconds: Int = userDefaults.value(forKey: "timeDiffSecondsDefault") as! Int? {
timeDiffSeconds = defaultTimeDiffSeconds
}
let productionTime = Calendar.current.date(byAdding: .second, value: timeDiffSeconds, to: Date())!
if store {
storeDateComponents(nowProdTime: productionTime)
}
productionTimeString = productionTimeFormatter.string(from: productionTime)
liveCounterButton.setTitle(productionTimeString, for: .normal)
}
Use the dedicated API which returns a non-optional
timeDiffSeconds = userDefaults.integer(forKey: "timeDiffSecondsDefault")
If a default value != 0 is required register it.
Note: Never use value(forKey with UserDefaults unless you really need KVC
When the key is absent, you are trying to force-cast an empty Any? to Int?, and thus, the if condition is not executed:
if let defaultTimeDiffSeconds: Int = userDefaults.value(forKey: "timeDiffSecondsDefault") as! Int? {
timeDiffSeconds = defaultTimeDiffSeconds
}
And if timeDiffSeconds was not initialized elsewhere, it will cause the crash when you try to use it.
The appropriate way would be conditional casting with as?:
if let defaultTimeDiffSeconds = userDefaults.object(forKey: "timeDiffSecondsDefault") as? Int { ... }
object(forKey:) was kindly suggested by Mr Leonardo.
Using userDefaults.integer(forKey: "timeDiffSecondsDefault") might be confusing when using timeDiffSeconds later, since integer(forKey:) would return 0 if the key is absent in user defaults, and returns an integer even if the value is a string or a boolean.

NSDate is uninitialized in swift 2.0

let theDate:NSDate = dateFor.dateFromString(date)! provides the error
unexpectadly found nil while unwrapping an optional value, theDate returns as uninitialized and as nil.
if let date = review.createdAt {
let dateFor:NSDateFormatter = NSDateFormatter()
dateFor.timeZone = NSTimeZone(abbreviation: "UTC")
dateFor.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let theDate:NSDate = dateFor.dateFromString(date)!
let tempoDate = Tempo(date: theDate)
let timeStamp = tempoDate.timeAgoNow()
if let reviewString = review.description {
let review = NSMutableAttributedString(string: reviewString + " - ")
let x = NSAttributedString(string: timeStamp, attributes: [NSForegroundColorAttributeName : UIColor.lightGrayColor()])
review.appendAttributedString(x)
vivrcell.reviewDescription.attributedText = review
vivrcell.reviewDescription.sizeToFit()
}
}
Why is this happening how do i fix it? this worked in swift 1.2
dateFor.dateFromString(date)!
returns nil because 'date' contains a wrongly formatted string which can not be converted to a date. But you are force unwrapping it, that creates the error. Instead of force unwrapping it with ! do it with an if let:
if let parsedDate = dateFor.dateFromString(date) { ... }
to check if the parsing works.

CMFormatDescription to CMVideoFormatDescription

I'm trying to get the resolution of the camera of a device using swift.
I'm using CMVideoFormatDescriptionGetDimensions which requires a CMVideoFormatDescription, but AVCaptureDevice.formatDescription returns a CMFormatDescription. I've tried a multitude of ways to cast CMFormatDescription to CMVideoFormatDescription and can't seem to get it working.
Below is a sample of the code that I'm using:
for format in device.formats as [AVCaptureDeviceFormat] {
let videoDimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
}
This doesn't seem possible in Swift at the moment. One solution then would be to write a helper function in objective-c, such as:
CMVideoDimensions CMFormatDescriptionGetDimensions(CMFormatDescriptionRef formatDescription)
{
if (CMFormatDescriptionGetMediaType(formatDescription) == kCMMediaType_Video)
return CMVideoFormatDescriptionGetDimensions(formatDescription);
else
return (CMVideoDimensions) {
.width = 0,
.height = 0
};
}
Include the header with the function prototype in the Swift bridging header so that it will be accessible as a global function from your Swift code.
I was able to get the resolution using the swift method below:
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo) as AVCaptureDevice
let formatDesc = captureDevice.activeFormat.formatDescription
let dimensions = CMVideoFormatDescriptionGetDimensions(formatDesc)
Here's a solution in pure Swift, really only usable for logging purposes and such. Paste the following function in your class or somewhere else:
func widthAndHeightFromTrack(track: AVAssetTrack) -> CGSize {
let str = track.formatDescriptions.description
let regex = try! NSRegularExpression(pattern: "[0-9]{2,4} x [0-9]{2,4}", options: [])
if let result = regex.firstMatchInString(str, options: [], range: NSMakeRange(0, str.characters.count)) {
let dimensionString = (str as NSString).substringWithRange(result.range)
let dimensionArray = dimensionString.componentsSeparatedByString(" x ")
let width = Int(dimensionArray[0])
let height = Int(dimensionArray[1])
return CGSize(width: width!, height: height!)
}
return CGSizeZero
}
Example usage:
let allTracks: AVAsset = someAVAsset.tracksWithMediaType(AVMediaTypeVideo)
let videoTrack = allTracks[0]
let videoTrackDimensions = widthAndHeightFromTrack(videoTrack)
// You now have a CGSize, print it
print("Dimensions: \(videoTrackDimensions)")
Of course, the above solution will completely break whenever Apple changes something in the string representation of the CMFormatDescription. But it's useful for logging the dimensions.
Maybe, question is too old, but the Swift issue is still not fixed.
public extension AVURLAsset {
var audioFormatDescription: CMAudioFormatDescription? {
if let track = self.tracks(withMediaType: .audio).first,
let untypedDescription = track.formatDescriptions.first {
// hacks, warnings, disablings of swiftlint below are wrork-around of
// Swift bug: it fails converting 'Any as CMFormatDescription'
let forceTyped: CMFormatDescription?
//swiftlint:disable force_cast
= untypedDescription as! CMAudioFormatDescription
//swiftlint:enable force_cast
if let description = forceTyped {
return description
} else {
return nil
}
} else {
return nil
}
}
}