I've got a working Apple Watch workout app. My metadata saves and all workout data flows to iPhone. I'm also able retrieve and display the data. But when i try to add arrays converted to ... json strings ... to metadata, the app crashes on save. Every time. I've tried numerous variations, always it's the same. Here's the latest code to crash... and it's perfectly fine.
GOOD CODE that works, but return string...
CRASHES with every HKWorkoutSession save.
func toJSON(array: [[String: Any]]) throws -> String {
let data = try JSONSerialization.data(withJSONObject: array, options: [])
return String(data: data, encoding: .utf8)!
}
NOW ... when my configuration class is converted with strings created using the function below, metadata saves just fine... and i'm back where i started. Wondering how to restore the [[String:Any]] arrays from String.
SAVES on WatchOS 3
This code creates a string from array of dictionaries.
What I'm needing help with is function to restore strings created using this function back into original form of [ [ String : Any ] ]
func joinedRepresentationOfArrayOfArrays(newArray: [[String : Any]]) -> String {
var newString = ""
for dictionary in newArray {
newString = newString.appending("[")
for (key, value) in dictionary {
newString = newString.appending("[\(key) : \(value), ")
}
newString = newString.appending("], ")
}
newString = newString.appending("], ")
return newString
}
Related
I am experiencing a bit of trouble, while working on my app in SwiftUI.
I want to append relevant data, summarized in an object, to an array and return this.
While returning the array, I could see by debugging, that it is empty. Debugging in the for loop showed me, that location objects are created and appended, but are not being "saved" in the array. The "mapItems" array on the other hand has lots of members.
What am I missing?
Here is the method I came up with:
func searchForLocation(searchTerm: String) -> Array<Location>{
var locations = [Location]
let searchReqeust = MKLocalSearch.Request()
searchRequest.region = region //region is a published variable and is determined before
searchRequest.naturalLanguageQuery = searchTerm
let search = MKLocalSearch(request: searchRequest)
search.start{response, error in
//error handling
.
.
.
//empty response.mapItems handling
.
.
.
for item in response!mapItems{
let location = createLocationFromItem(item: item)
locations.append(location)
}
}
return locations
}
My locations class if following:
class Location: Identifiable{
var id= UUID()
var coordinates: CLLocationCoordinate2d
//And its proper init method
}
Your searchForLocation has an asynchronous function inside (search.start{...}),
and your code returns before it has finished getting the results.
To "wait" for the results use a completion/closure handler,
something like this:
func searchForLocation(searchTerm: String, completion: #escaping ([Location]) -> ()) {
var locations = [Location]() // <-- here
// ....
search.start{response, error in // <-- here asynchronous function
//... todo deal with errors, eg return completion([])
for item in response!mapItems {
let location = createLocationFromItem(item: item)
locations.append(location)
}
completion(locations) // <- here return when finished
}
}
and call the function like this:
searchForLocation(searchTerm: "Tokyo") { results in
print("\(results)") // <-- here results available, not before
}
I suggest you read-up on how to create and use asynchronous functions, these are important concepts to master to code effectively in Swift.
I have following problem:
I have iOS application with C++ core. Data is bridged through C++<->ObjCpp<->ObjC chain, data structures are later imported in swift from ObjC.
I have optional<vector<optional>> in one of my structures and there is problem with importing it. Vector is converted to NSList, which is populated by data. Because there can't be nil in NSList, I add NSNull to it. If vector isn't optional - I can work with it in Swift, though it isn't very convenient.
let arr = struct.myArray as NSArray
var swift_arr: [UUID?] = []
for val in arr {
if val is NSNull {
swift_arr.append( nil )
} else if val is NSUUID {
swift_arr.append( val as? UUID )
} else {
fatalError()
}
}
But if array is optional - there is big problem, because when I try to bind optional value, Swift tries to convert it to [UUID?] from NSArray, and when there is NSNull among NSUUIDs, it crashes, because NSNull cannot be converted to UUID.
Is there any proper way to handle such arrays?
I am trying to set an array as an environmental variable in postman.
But it stores the first value of the array rather than the array.
var aDataEntry = postman.pm.environment.get('data_set_entries');
if(aDataEntry == null) {
aDataEntry = [];
}
var jsonData = pm.response.json();
aDataEntry.push(jsonData.dataEntry.id);
// a console.log here confirms that aDataEntry is an array
postman.pm.environment.set('data_entry',aDataEntry);
As mentioned in the comment of the code, the variable is coming as an array,
but when I again get the environment variable in the second run, it is not
of type array. But just contains the first element in the array.
What's wrong here?
How can set the array and use it from the postman environment variable.
It seems like pm.environment.set calls toString to set an environment value. You can use the below code to work-around that:
var aDataEntry = pm.environment.get('data_set_entries');
if(aDataEntry == null) {
aDataEntry = [];
} else {
aDataEntry = JSON.parse(aDataEntry);
}
var jsonData = pm.response.json();
aDataEntry.push(jsonData.dataEntry.id);
// a console.log here confirms that aDataEntry is an array
pm.environment.set('data_entry',JSON.stringify(aDataEntry));
Edit 1:
As mentioned in the Postman reference docs, it is suggested that one use JSON.stringify() and JSON.parse() for storing complex objects. I have updated the code accordingly.
I'm not sure of how you intend to use the array, but to dynamically generate an array for use in a Body > raw > JSON POST, as in the answer above you do need to actually store the var as a string.
Here's an example of that and it's use in the POST Body. I had a long list of IDs, and I'm using Postman to do some bulk user profile updates.
In the Pre-request Script, generate the string to be POSTed as an array.
var externalIds = [111,222,333,444];
var attrString = "";
externalIds.forEach(userId => {
attrString += `,{"external_id": ${userId},"my_first_attribute": false,"my_next_attribute": true}`;
});
attrString = attrString.replace(',',''); // strip out that 1st unwanted comma
pm.environment.set("attributeArray",attrString);
The saved "array", Postman console logged:
"{"external_id": 111,"my_first_attribute": false,"my_next_attribute": true},
{"external_id": 222,"my_first_attribute": false,"my_next_attribute": true},
{"external_id": 333,"my_first_attribute": false,"my_next_attribute": true},
{"external_id": 444,"my_first_attribute": false,"my_next_attribute": true}"
Looks like bad, nested double quotes, but the format is actually valid.
My Body > raw looks like:
{
"api_key": "{{api_key}}",
"attributes": [{{attributeArray}}]
}
Note the Postman variable is wrapped in "[" and "]".
If my externalIds array needed to be a pm variable, I'd store that as a string, and .split() it when using it in the Script tab.
The Postman console really helps get past the syntax mistakes.
I'm trying to save data in an image metadata in iOS/Swift3. It does not appear that CG will let you save out custom tags (is that true?) so I JSON encoded my dictionary and put the result as a string into the TIFF tag's ImageDescription. When I load the image and get the metadata back...
if let data = NSData(contentsOfFile:oneURL.path), let imgSrc = CGImageSourceCreateWithData(data, options as CFDictionary) {
let allmeta = CGImageSourceCopyPropertiesAtIndex(imgSrc, 0, options as CFDictionary) as? [String : AnyObject]
The allMeta contains (among other things):
▿ 0 : 2 elements
- key : ImageDescription
- value : {
"CameraOrientationW" : 0.1061191,
"CameraOrientationZ" : -0.01305595,
"CameraOrientationX" : 0.01319851,
"CameraOrientationY" : 0.9941801
}
Which has the JSON data, yay! So now I simply have to get the TIFF metadata, get the ImageDescription from that, and de-JSON it...
let tiffmeta = allmeta?["{TIFF}"]
if let tiffMeta = tiffmeta {
let descmeta = tiffMeta["ImageDescription"]
var descdata = descmeta?.data(usingEncoding: NSUTF8StringEncoding)!
let descdict = try? JSONSerialization.jsonObject(with: descdata, options: [])
But this will not compile. Xcode puts an error on the let descdata line:
Value of type 'MDLMaterialProperty??' has no member 'data'
I tried casting it to String on the line above, at which point it complains I didn't unwrap the optional MDLMaterialProperty.
Am I missing something obvious here?
So just to close this one, this appears to be a problem in the compiler. I made a number of minor changes to the syntax, nothing that had any actual effect on the code, and suddenly it decided the object was indeed a string.
I have a view page that currently has two columns of data shown, soon to be expanded to four. Each column contains the result of a QuerySet for that particular model.
Here's what I have in my views.py method:
if request.REQUEST["type"] == "text":
client = Client.objects.get(client_name = request.REQUEST["search"])
peerList = ClientPeers.objects.prefetch_related().filter(client = client.client)
compList = ClientCompetitors.objects.prefetch_related().filter(client = client.client)
else:
peerList = ClientPeers.objects.prefetch_related().filter(client = request.REQUEST["search"])
compList = ClientCompetitors.objects.prefetch_related().filter(client = request.REQUEST["search"])
for peer in peerList:
peerlst.append({"pid" : peer.parentorg.parentorg, "pname" : peer.parentorg.parentorgname})
for comp in compList:
complst.append({"cid" : comp.parentorg.parentorg, "cname" : comp.parentorg.parentorgname})
lst.append(simplejson.dumps(peerlst))
lst.append(simplejson.dumps(complst))
return HttpResponse(simplejson.dumps(lst), mimetype = "text/json")
This allows me to send a 2D array of data to the browser in the format
[ { //JSON }, { //JSON } ]
In my jQuery.ajax success function, I have
function handler(results) {
var data = JSON.parse(results);
for (var i = 0; i < data[0].length; i++)
$("#available_peers").append("<li>" + data[0][i].pname + "</li>");
for (var i = 0; i < data[1].length; i++)
$("#available_competitors").append("<li>" + data[1][i].cname + "</li>");
Firebug shows that the GET request works and I can see the data in the response tab. However, the console prints out
SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data
var data = JSON.parse(results)
This error disappears if I replace var data = JSON.parse(results) with
var peers = JSON.parse(data[0]);
var comps = JSON.parse(data[1]);
Why does one method work but another doesn't?
The jQuery ajax() call will make an intelligent guess as to the returned data type. In your example, function handler(results), the results variable will already be a decoded JSON object, containing two items in an array. The reason that JSON.parse(data[0]) works, is that you have returned JSON encoded data as a string.
Don't encode the individual list elements to JSON before placing in the output array:
lst.append(peerlst) # <-- Don't encode to JSON string here
lst.append(complst)
return HttpResponse(simplejson.dumps(lst), mimetype = "application/json") # <-- Single JSON encoding