NSUserDefafults returns wrong data - swift

On my Cocoa application using Swift, I'm getting bad data from NSUserDefaults. If I go to the command line and do
defaults read my.bundle.identifier
It comes back with this:
{
NSNavLastRootDirectory = "~/Documents";
NSNavPanelExpandedSizeForSaveMode = "{712, 521}";
producer = 10;
version = "-1";
when = 1470502459;
}
Then, in my application I read the value of version like so:
let def = NSUserDefaults.standardUserDefaults()
let current = def.integerForKey("version")
print("Current version is \(current)")
It's printing out that current is 3, not -1. What in the world is going on?

Related

Get user preferred temperature setting in macOS

I'm trying to read the user set system preferences for Temperature unit (Celsius/Fahrenheit).
I was trying to get this data using NSLocale but I cannot find any evidence of a temperature setting in there.
Is it even possible to read this data?
Thanks!
The official API is documented under the Preferences Utilities:
let key = "AppleTemperatureUnit" as CFString
let domain = "Apple Global Domain" as CFString
if let unit = CFPreferencesCopyValue(key, domain, kCFPreferencesCurrentUser, kCFPreferencesAnyHost) as? String {
print(unit)
} else {
print("Temperature unit not found")
}
If you wonder how I found it, I used the defaults utility in the Terminal:
> defaults find temperature
Found 1 keys in domain 'Apple Global Domain': {
AppleTemperatureUnit = Fahrenheit;
}
Found 1 keys in domain 'com.apple.ncplugin.weather': {
WPUseMetricTemperatureUnits = 1;
}
This is a bit of a hack, but you can do it this way on macOS 10.12+ and iOS 10+:
// Create a formatter.
let formatter = MeasurementFormatter()
// Create a dummy temperature, the unit doesn't matter because the formatter will localise it.
let dummyTemp = Measurement(value: 0, unit: UnitTemperature.celsius)
let unit = formatter.string(from: dummyTemp).characters.last // -> F
This outputs "F" in my playground which defaults to the US locale. But change your locale or use this code on a device and you'll get the locale specific temperature unit - or the string for it anyway.

Bypassing code on initial run

In my code I need to retrieve a saved array of data to populate student history data. I am using the following line of code - which works great.
returnedArray = UserDefaults.standard().object(forKey: "studentHistoryArray")! as! NSArray as! [[String]]
The problem I have is on the initial (first) run of the program. The array hasn't been created/saved yet so I need to skip this step, but only on the initial run of the program. Is there a way to run this line of code only on the first run of a program?
var defaults = UserDefaults.standard()
let studentHistoryArrayKey = "studentHistoryArray"
var returnedArray = defaults.object(forKey: studentHistoryArrayKey) as? [[String]]
// I don't think that you need to use the intermediary cast to NSArray, but
// I could be wrong.
if returnedArray == nil {
returnedArray = [[String]]()
defaults.setObject(returnedArray, forKey: studentHistoryArrayKey)
}
// Now it's guaranteed to be non-nil, so do whatever you need to do with the array.
As a rule of thumb, if you're using ! as liberally as in your example, something's going to break.

Error accessing recordFields in CKQueryNotification

When I access the recordFields on a CKQueryNotification returned from a subscription, I get this error:
Could not cast value of type '__NSCFDictionary' (0x1009f0630) to 'CKRecordValue' (0x1009fc050).
I process the notification like this:
let notification: CKNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String : NSObject])
It seems to work fine and I get a CKQueryNotification.
The notification recordFields prints out like:
<CKQueryNotification: 0x156ef2bf0; notificationType=1, notificationID=<CKNotificationID: 0x156ef41e0; UUID=ba82325d-ab9e-4805-bb26-994a1122900b>, containerIdentifier=iCloud.com.skyvalleystudio.RaceTiming, subscriptionID=Marks for 06597454-4EAD-434E-BC03-CE51D987C79F, queryNotificationReason=1, recordFields={
location = {
altitude = 0;
course = "-1";
horizontalAccuracy = 0;
latitude = "37.33296424";
longitude = "-122.03937369";
speed = "-1";
timestamp = "484712160.263403";
verticalAccuracy = "-1";
};
timeStamp = 484712160;
user = "_2031e89e9ade8345c7d4248f3ec3a2e6";
}, recordID=<CKRecordID: 0x156ef1670; 568D257C-E849-4492-A810-CB7076FC5A22:(_defaultZone:__defaultOwner__)>, isPublicDatabase=1>
So it looks like the recordFields dictionary is there and populated.
I try to access is like this:
let timestamp = notification.recordFields!["timeStamp"] as? NSDate
Then I get the error.
UPDATE
I filed a Apple bug report 26251611.
The CLLocation is not being decoded in the CKNotification creation is my best guess.
One possible workaround is processing the userInfo directly, but I had problems with processing the CKReference. It seems like the CKRecordValue protocol adopters should be better handled when the CKNotification is created.
UPDATE
Closed as duplicate of 24643471. It's still open at this time...

PMCreateSession not creating print session

Trying to get printable rect for OS X app. Seems to involve creating a session, then a page format, validating the format, etc. Code compiles, but getting a status of -50 from PMCreateSession. Am I declaring printSession improperly? Normally don't have to deal so much with UnsafeMutablePointers.
Thanks!
let printSession: UnsafeMutablePointer<PMPrintSession> = nil
let pmPageFormat: UnsafeMutablePointer<PMPageFormat> = nil
var status = PMCreateSession(printSession)
status = PMCreatePageFormat(pmPageFormat)
status = PMSessionDefaultPageFormat(printSession.memory, pmPageFormat.memory)
let changed: UnsafeMutablePointer<DarwinBoolean> = nil
status = PMSessionValidatePageFormat(printSession.memory, pmPageFormat.memory, changed)
changed.destroy()
var pRect = PMRect()
status = PMGetAdjustedPageRect(pmPageFormat.memory, &pRect)
Swift.print("pRect \(pRect)")
status = PMRelease(pmPageFormat)
status = PMRelease(printSession)
Am I declaring printSession improperly?
One could acquire a PMPrintSession as follows:
// create a C Null pointer of type PMPrintSession
let printSession = unsafeBitCast(0, to: PMPrintSession.self)
// pass by & converts PMPrintSession to UnsafeMutablePointer<PMPrintSession>
PMCreateSession(&printSession)
…
// recast printSession to release memory
PMRelease( PMObject(printSession) )
Alternately, a PMPrintSession can be accessed from the Cocoa NSPrintInfo:
let printInfo = NSPrintInfo.shared()
let printSession = PMPrintSession(printInfo.pmPrintSession())
Normally don't have to deal so much with UnsafeMutablePointers
For information more information on using an UnsafeMutablePointer see StackOverflow "How to use UnsafeMutablePointer in Swift?"
For a complete example of using Core Printing with Swift see 004.42Apple_CorePrintingExample on GitHub.

Xcode 6.1 / iOS8.1.1 / Swift - issue running code on device but works fine in all simulators

I have this very strange problem running my application on my iPhone 6 running iOS 8.1.1 and Xcode 6.1.
My app runs fine on multiple devices in the simulator but when I run on a physical device (iPhone 6 or iPad 4) I get the problem.
This code was working fine in Objective C and have recently converted it to Swift which is when the problems started.
Please see an extract of code below which is causing the problem.
// ======================================================================
// SAVE CHANGES
// ======================================================================
func saveDeviceChanges () {
//var deviceData : NSMutableDictionary
var deviceFound : Bool = false
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
// Update friendly names array
for var i = 0; i < _deviceFriendlyNames.count; i++ {
var deviceData: Dictionary = _deviceFriendlyNames.objectAtIndex(i).mutableCopy() as NSDictionary
let usn: String = deviceData["USN"] as NSString
let availDevice : String = delegate._selectedSerialNumber
if usn == availDevice {
// Update value in dictionary
**// THIS LINE 2**
**deviceData.updateValue(txtFriendlyName.text, forKey: "FriendlyName")**
// Update array
_deviceFriendlyNames.replaceObjectAtIndex(i, withObject: deviceData)
deviceFound = true
}
}
// Add new device to friendly names array
if deviceFound == false {
let newDevice: Dictionary = ["USN": lblUSN.text, "FriendlyName": txtFriendlyName.text]
_deviceFriendlyNames.addObject(newDevice)
}
// Save Changes back to plist
let documentDirectoryPath : AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0]
let filePath:NSString = documentDirectoryPath.stringByAppendingString("NowRemote.plist")
// Create dictionary to hold the plist data
**// THIS LINE 1**
**var pListData : NSMutableDictionary = NSDictionary(contentsOfFile: filePath)?.mutableCopy() as NSMutableDictionary**
// Update Devices list back into pList root
//pListData?.updateValue(_deviceFriendlyNames, forKey: "Devices")
pListData.setObject(_deviceFriendlyNames, forKey: "Devices")
// Write Data
pListData.writeToFile(filePath, atomically: true)
delegate._selectedDeviceName = ""
delegate._selectedDeviceAddress = ""
delegate._selectedSerialNumber = ""
delegate._reloadData = true
}
The problem starts when it gets to the line which I've put a comment (THIS LINE #1), I have put a breakpoint on this line and then click the step into button to see which line gets executed next. Strangely it jumps to the line (THIS LINE #2) which is inside an IF statement which is inside a FOR loop, then an error gets thrown of "Thread 1: EXC_Breakpoint (Code =1, subcode = 0x1000767d4).
I have no idea why when running on my device it's jumping back in the code to this statement? If I run the code in the simulators it works fine and goes through the statements correctly.
I've been scratching my head with this for days so would be extremely grateful if anyone has any idea why this is happening?
Problem resolved by "holex" (thank you!) - replaced "stringByAppendingString" with "stringByAppendingPathComponent"