Swift-Can't convert core data to url parameters - ios

I am trying to build a small app that track device location. So save the location into core data. But can't convert it to json for sending this information to server. I got following errors
2018-12-18 13:08:33.583580+0530 My App[6696:144278] [General] An uncaught exception was raised.
2018-12-18 13:08:33.583605+0530 My App[6696:144278] [General] Invalid type in JSON write (__NSDate).
2018-12-18 13:08:33.604814+0530 My App[6696:144278] [General] (
0 CoreFoundation 0x00007fff4af112db __exceptionPreprocess + 171 ........)
Loading data from core data
func loadingFromCoreData() -> [String:Dictionary<String,AnyObject>]
{
var locationsArray: [String:Dictionary<String,AnyObject>] = [:]
var count = 0
if let context = (NSApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext {
do {
locationDataInstance = try context.fetch(LocationInformation.fetchRequest())
} catch {
}
}
print("IN Loading From database")
for locationDI in locationDataInstance {
var location_info: [String:AnyObject]=[:]
location_info["latitude"] = locationDI.latitude as AnyObject
location_info["longitude"] = locationDI.longitude as AnyObject
location_info["timestamp"] = locationDI.timestamp as AnyObject
locationsArray["\(count)"] = location_info
count = count + 1
}
return locationsArray
}
Dictionary to Json coversion
let parameters:Dictionary<String,AnyObject> = ["name":devicename as AnyObject,"loctioninfo":loadingFromCoreData() as AnyObject]
do {
let js = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
print(js)
} catch {
print(error.description)
}

The error is pretty clear: The type of timestamp is obviously Date and JSON does not support this type.
Add a computed property to convert the Date object to UNIX timestamp or date string for example
var unixTimeStamp : Double {
return timestamp.timeIntervalSince1970
}
And get rid of this ugly as AnyObject cast by declaring the dictionary as [String:Any]

Related

How Fix Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 52."

How to Convert this one. "{\n ID = \"d9a7c7bf-781d-47b3-bb4e-e1022ec4ce1b\";\n Name = Headquarters;\n}"; To this format {
"ID": "d9a7c7bf-781d-47b3-bb4e-e1022ec4ce1b",
"Name": "Headquarters"
}
if let jsonString = text as? String {
let objectData = jsonString.data(using: String.Encoding.utf8)
do {
let json = try JSONSerialization.jsonObject(with: objectData!, options: .allowFragments) as! [String:Any] //try JSONSerialization.jsonObject(with: objectData!, options: JSONSerialization.ReadingOptions.mutableContainers)
print(String(describing: json))
return json
} catch {
// Handle error
print(error)
}
}
Blockquote
First of all and already mentioned the string format is clearly not JSON.
It's the string format which is returned when calling the description property of a Foundation collection type (NSArray / NSDictionary).
For example a print statement calls description and this format appears also in output of Terminal.app.
However there is a solution: This string format is called openStep (an OpenStep / NeXt legacy format) and is available in PropertyListSerialization
This code reads the format:
let string = "{\n ID = \"d9a7c7bf-781d-47b3-bb4e-e1022ec4ce1b\";\n Name = Headquarters;\n}"
let data = Data(string.utf8)
do {
let dictionary = try PropertyListSerialization.propertyList(from: data, format: nil)
print(dictionary)
} catch { print(error) }
Note:
I'm pretty sure that the original data format is not openStep and somewhere you created the string unnecessarily with the String(describing initializer like in the question.
your json format is incorrect. If you try it with jsonformatter it will throw this error:
so first you need to replace ; with ,. The second is that Strings should be wrapped in double quotes, replace Name = Headquarters with Name = "Headquarters".
This is the right form
{\n ID = \"d9a7c7bf-781d-47b3-bb4e-e1022ec4ce1b\",
\n Name = "Headquarters"\n}

NSNetService dictionaryFromTXTRecord fails an assertion on invalid input

The input to dictionary(fromTXTRecord:) comes from the network, potentially from outside the app, or even the device. However, Apple's docs say:
... Fails an assertion if txtData cannot be represented as an NSDictionary object.
Failing an assertion leaves the programmer (me) with no way of handling the error, which seems illogic for a method that processes external data.
If I run this in Terminal on a Mac:
dns-sd -R 'My Service Name' _myservice._tcp local 4567 asdf asdf
my app, running in an iPhone, crashes.
dictionary(fromTXTRecord:) expects the TXT record data (asdf asdf) to be in key=val form. If, like above, a word doesn't contain any = the method won't be able to parse it and fail the assertion.
I see no way of solving this problem other than not using that method at all and implementing my own parsing, which feels wrong.
Am I missing something?
Here's a solution in Swift 4.2, assuming the TXT record has only strings:
/// Decode the TXT record as a string dictionary, or [:] if the data is malformed
public func dictionary(fromTXTRecord txtData: Data) -> [String: String] {
var result = [String: String]()
var data = txtData
while !data.isEmpty {
// The first byte of each record is its length, so prefix that much data
let recordLength = Int(data.removeFirst())
guard data.count >= recordLength else { return [:] }
let recordData = data[..<(data.startIndex + recordLength)]
data = data.dropFirst(recordLength)
guard let record = String(bytes: recordData, encoding: .utf8) else { return [:] }
// The format of the entry is "key=value"
// (According to the reference implementation, = is optional if there is no value,
// and any equals signs after the first are part of the value.)
// `ommittingEmptySubsequences` is necessary otherwise an empty string will crash the next line
let keyValue = record.split(separator: "=", maxSplits: 1, omittingEmptySubsequences: false)
let key = String(keyValue[0])
// If there's no value, make the value the empty string
switch keyValue.count {
case 1:
result[key] = ""
case 2:
result[key] = String(keyValue[1])
default:
fatalError()
}
}
return result
}
I'm still hoping there's something I'm missing here, but in the mean time, I ended up checking the data for correctness and only then calling Apple's own method.
Here's my workaround:
func dictionaryFromTXTRecordData(data: NSData) -> [String:NSData] {
let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes), count: data.length)
var pos = 0
while pos < buffer.count {
let len = Int(buffer[pos])
if len > (buffer.count - pos + 1) {
return [:]
}
let subdata = data.subdataWithRange(NSRange(location: pos + 1, length: len))
guard let substring = String(data: subdata, encoding: NSUTF8StringEncoding) else {
return [:]
}
if !substring.containsString("=") {
return [:]
}
pos = pos + len + 1
}
return NSNetService.dictionaryFromTXTRecordData(data)
}
I'm using Swift 2 here. All contributions are welcome. Swift 3 versions, Objective-C versions, improvements, corrections.
I just ran into this one using Swift 3. In my case the problem only occurred when I used NetService.dictionary(fromTXTRecord:) but did not occur when I switched to Objective-C and called NSNetService dictionaryFromTXTRecord:. When the Objective-C call encounters an entry without an equal sign it creates a key containing the data and shoves it into the dictionary with an NSNull value. From what I can tell the Swift version then enumerates that dictionary and throws a fit when it sees the NSNull. My solution was to add an Objective-C file and a utility function that calls dictionaryFromTXTRecord: and cleans up the results before handing them back to my Swift code.

Can't figure out why I get a fatal error: unexpectedly found nil while unwrapping an Optional value

I keep getting this error :
fatal error: unexpectedly found nil while unwrapping an Optional value
and cannot figure out how to debug it!
Here's my code :
func readCSV() -> Array<String> {
// Creates a new array of strings
var csvArray : Array<String> = Array<String>()
if let url: NSURL = NSURL(string : "URLFROMCSV" ) {
// Creates an Input Stream that will load the datas from our URL
let data :NSData! = NSData(contentsOfURL: url)!
let stream : NSInputStream! = NSInputStream(data: data)
// Opens the receiving stream
stream.open()
// Sets a buffer with a given size size
let bufferSize = 1024
var buffer = Array <UInt8>(count: bufferSize, repeatedValue: 0)
// String variable initialization
var csvFullString : String = ""
// While the stream receives datas, parses datas, convert them into strings and then concatenate them into one big string
while (stream.hasBytesAvailable) {
let readSize = stream.read(&buffer, maxLength: bufferSize)
let csvRaw = NSString (bytes: &buffer, length: readSize, encoding: NSUTF8StringEncoding)
let csvString = csvRaw as String!
csvFullString = csvFullString + csvString
}
// Fills the array with each strings. Separation between strings is made when a Θ character is parsed
csvArray = csvFullString.componentsSeparatedByString("Θ")
// Delete each null string
for(var i = 0 ; i < csvArray.count; i++) {
if(csvArray[i] == "") {
csvArray.removeAtIndex(i)
}
}
}
return csvArray
}
After searching on the web, I'm pretty sure it has something to do with unwrapping elements but the fact is when I debug it, i don't get any nil value anywhere.
PS: Would like to upload a screen but can't because i don't have 10 reputation, so bad!
Thanks in advance!
EDIT : Line let data :NSData! = NSData(contentsOfURL: url)! got the error.
Terry
You're probably creating the error in one of these two lines (though it may show up later):
let data :NSData! = NSData(contentsOfURL: url)!
let stream : NSInputStream! = NSInputStream(data: data)
You're assigning an optional value to an implicitlyUnwrappedOptional type and then using it without checking if you have a valid value.
This is why if let exists. It's a little funny that you've started to indent as if you're using if let but aren't.
Try this instead:
if let url = NSURL(string : "http://gorillaapplications.com/etablissements.csv" ) {
// Creates an Input Stream that will load the datas from our URL
if let data = NSData(contentsOfURL: url) {
let stream = NSInputStream(data: data)
stream.open()
// rest of your code here
}
else {
println("Didn't get a data object")
}
}
else {
println("Didn't get a URL object")
}
You really need to grasp Optionals for Swift. I'd recommend reading my Optionals chapter in this iBook: https://itunes.apple.com/us/book/swift-optionals-generics-for/id943445214?mt=11&uo=4&at=11lMGu
Update:
Since you added a bit more in your comments above, you're saying you get the error on this line: let data: NSData! = NSData(contentsOfURL: url)!. This is because of the ! at the end, which tells Swift you're sure that this function will return a valid value, so just use it, without checking if it's nil first. In your case, the function is returning nil and so your app crashes. Using the sample code I've provided above, you'll see that you'll no longer get a crash, but your code will execute the "Didn't get a data object" line. You'll need to correctly handle the case where you can't load data from that URL.

RestKit relationships not mapping when manually mapping JSON object

I am following some suggested posts on how to map incoming JSON data directly using RKMapperOperation. My entity object is being created in the data store, but without the proper relationships. Interestingly, if I create an object directly using the Core Data methods after I've already mapping incoming JSON (via websockets), the operation seems to "flesh" out my relationships in the incorrect entity.
To sum the order:
JSON data comes into app through a websocket connection
I map it using the below code, but the relationships aren't mapped
I save some other record in the same entity using a locally created (not RestKit) object with Core Data.
My object mapped from JSON now has its relationships attached!
Here is the JSON data:
{
"checkin": {
"session_id": 1,
"attendee_id": 70,
"source": "list",
"created_at": "2015-03-26 11:53:08",
"cache_id": "9234d700852df5c7402b87adf6ecfc19",
"checkout": "0",
"updated_at": "2015-03-27 03:53:09",
"id": 359
}
}
Here is my mapping function
func mapEntityFromJson(JSONString: String, key: String, mapping: RKEntityMapping!) -> NSManagedObject? {
let MIMEType = "application/json"
var error: NSError? = nil
let data = JSONString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let jsonDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSDictionary
let parsedData: AnyObject! = RKMIMETypeSerialization.objectFromData(data, MIMEType: MIMEType, error: &error)
if (parsedData == nil && error != nil) {
// Parser error...
return nil
}
let mappingsDictionary = [ key: mapping ]
let mappingDS = RKManagedObjectMappingOperationDataSource(managedObjectContext: self.objectStore.persistentStoreManagedObjectContext, cache: self.objectStore.managedObjectCache)
let mapper = RKMapperOperation(representation: parsedData, mappingsDictionary: mappingsDictionary)
mapper.mappingOperationDataSource = mappingDS
var mappingError: NSError? = nil
let isMapped = mapper.execute(&mappingError)
if (isMapped && mappingError == nil) {
// Trying to save twice per some other example
self.objectStore.persistentStoreManagedObjectContext.save(&error)
self.objectStore.persistentStoreManagedObjectContext.saveToPersistentStore(&error)
let result = mapper.mappingResult.firstObject() as NSManagedObject
return result
}
return nil
}
Here is the relationship mapping I'm passing to this function:
func checkinMapping() -> RKEntityMapping {
let checkinMapping = RKEntityMapping(forEntityForName: "Checkin", inManagedObjectStore: objectStore)
let checkinDictionary = ["source": "source", "checkout": "checkout", "cache_id": "cacheId", "attendee_id": "attendeeId", "session_id": "sessionId"]
checkinMapping.addAttributeMappingsFromDictionary(baseRecordDictionary)
checkinMapping.addAttributeMappingsFromDictionary(checkinDictionary)
checkinMapping.addConnectionForRelationship("attendee", connectedBy: ["attendeeId": "id"])
checkinMapping.addConnectionForRelationship("session", connectedBy: ["sessionId": "id"])
checkinMapping.identificationAttributes = ["cacheId"]
checkinMapping.setDefaultValueForMissingAttributes = true
return checkinMapping
}
Here is how the function gets called when the websocket subscription is notified:
let jsonString = "<the JSON data per above>"
let mappingResult = self.mapEntityFromJson(jsonString, key: "checkin", mapping: self.checkinMapping())
The attendee_id and session_id values should be establishing a relationship with Attendee and Session entities, but when I look at the sqlite data underlying Core Data the relationship columns are blank even though the incoming attendeeId and sessionId fields get mapped. Once I make the other save locally then those relationship columns get mapped.
Any thoughts?
EDIT:
I should add that when a checkin is mapped from a full RestKit call like .getObject or as the result of .postObject then the mapping works with no issues. It is only in the manual mapping I have here that it seems to fall apart.
Here is the final solution which involved much help from #wain and a key point from another post about needing to make the operationQueue wait until all operations are finished or else the mapping process hadn't finished mapping the relationships. So my NSManagedObject was returning with no relationships connected. The combination of a private child context for proper concurrency and saving of data along with the waiting issue fixed the problem. Here is the final mapping code:
func mapEntityFromJson(JSONString: String, key: String, mapping: RKEntityMapping!) -> NSManagedObject? {
let MIMEType = "application/json"
var error: NSError? = nil
let data = JSONString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let jsonDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSDictionary
let parsedData: AnyObject! = RKMIMETypeSerialization.objectFromData(data, MIMEType: MIMEType, error: &error)
if (parsedData == nil && error != nil) {
// Parser error...
return nil
}
let mapperContext = self.objectStore.newChildManagedObjectContextWithConcurrencyType(NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType, tracksChanges: true)
let mappingsDictionary = [ key: mapping ]
let mappingDS = RKManagedObjectMappingOperationDataSource(managedObjectContext: mapperContext, cache: self.objectStore.managedObjectCache)
var mappingResult: RKMappingResult? = nil
var result: NSManagedObject? = nil
let mapper = RKMapperOperation(representation: parsedData, mappingsDictionary: mappingsDictionary)
mappingDS.operationQueue = self.operationQueue
mappingDS.parentOperation = mapper
mapper.mappingOperationDataSource = mappingDS
var mappingError: NSError? = nil
let isMapped = mapper.execute(&mappingError)
// Necessary to wait for relationships to map.
if self.operationQueue!.operationCount > 0 {
self.operationQueue!.waitUntilAllOperationsAreFinished()
}
if (isMapped && mappingError == nil) {
mapperContext.saveToPersistentStore(&error)
mappingResult = mapper.mappingResult
}
if mappingResult != nil {
result = mappingResult!.firstObject() as? NSManagedObject
}
return result
}
For now we're leaving it with the in-memory cache and don't see the duplicates issue others reported.

Error on serializer JSON using Swift - AutoreleasingUnsafePointer (has 1 child)

I was try parse a JSON online, but cannot read this how the types generally, how:
typealias JSON = AnyObject
typealias JSONDictionary = Dictionary<String, JSON>
typealias JSONArray = Array<JSON>
Never return this types, the JSON i read is:
json:
[
{
"id":72,
"name":"Batata Cremosa",
"time":"1:30 horas",
"rcp_img_file_name":"batata-cremosa.jpg"
},
{
"id":183,
"name":"Caldeirada de Peixes",
"time":"50 minutos",
"rcp_img_file_name":"caldeirada-peixes.jpg"
},
{
"id":76,
"name":"Batata com Cebola e Ervas",
"time":"10 minutos",
"rcp_img_file_name":"batata-cebola-ervas.jpg"
},
{
"id":56,
"name":"Arroz de forma",
"time":"25 minutos",
"rcp_img_file_name":"arroz-forma.jpg"
}]
The json is read and cast in a string how this in console:
in console(var jsonString):
(
{
id = 72;
name = "Batata Cremosa";
"rcp_img_file_name" = "batata-cremosa.jpg";
time = "1:30 horas";
},
{
id = 183;
name = "Caldeirada de Peixes";
"rcp_img_file_name" = "caldeirada-peixes.jpg";
time = "50 minutos";
},
{
id = 76;
name = "Batata com Cebola e Ervas";
"rcp_img_file_name" = "batata-cebola-ervas.jpg";
time = "10 minutos";
},
{
id = 463;
name = "Pat\U00ea de Frango F\U00e1cil";
"rcp_img_file_name" = "pate-frango-facil.jpg";
time = "30 minutos";
},
...
So, this is the func that dont read the JSON, by debug when have to choice around the Objects dont enter in anyone option:
parse function:
func JSONParseArray(jsonString: String) {
println(jsonString)
var data: NSData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
var error: AutoreleasingUnsafePointer<NSError?> = nil
let jsonObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0),
error: error)
println("Error: \(error)")
if jsonObj is JSONDictionary {
println("0")
}
else if jsonObj is JSONArray {
println("1")
}
}
Dont return Array or Dictionary, in debug appear this below the json:
error returned:
Error: VSs26AutoreleasingUnsafePointer (has 1 child)
and when i debug the jsonObj this return nil
Please someone help, i search by hours by info, test many things but no one works for me, thanks much.
It is easier than you think:
var error: NSError?
let jsonObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0),
error: &error)
Additionally there is the question of why the JSON is being passed in as a string and not data.
jsonObj is a bad name because once it is de-serialized it is going to be an Array, not JSON.

Resources