Storing NSArray in UIPasteboard - nsarray

I have several text files which I want to transfer between 2 Apps. (ie. free and paid versions of the same App).
I'm using UIPasteboard to do this. The contents of the files are held in memory as NSArrays, and so I want to copy these NSArrays to the pasteboard (lite version), and read them from the pasteboard (full version).
For some reason the data cannot be read back from the pasteboard. The data is being returned as a NSData object, rather than NSArray, which I think means that it is not in the required format for the pasteboard type I am using, which is "public.utf8-plain-text".
When I read/write NSStrings with this pasteboard type, it works fine.
I searched through Apple docs, etc, to see if there is a different type I should be using for NSArrays, (or other property list objects), but drew a blank.
Writing to the pasteboard: (In the following pDataOutput is an array of strings, file contents) :
NSMutableArray *lArrayCopy = [gGlobalData.cPasteBoard.items mutableCopy];
[lArrayCopy replaceObjectAtIndex:pDataFileIdx
withObject:[NSDictionary dictionaryWithObject:pDataOutput
forKey:#"public.utf8-plain-text"]];
gGlobalData.cPasteBoard.items = lArrayCopy;
[lArrayCopy release];
Reading from the pasteboard:
NSArray *lPBItems = [pPasteBoard valuesForPasteboardType:#"public.utf8-plain-text"
inItemSet:nil];
NSLog(#"PB Items = NSArray of count %d", lPBItems.count);
The above returns:
PB Items = NSArray of count 0
As mentioned above, it returns the data correctly as NSStrings if written as NSStrings.
Any help would be very much appreciated.
Thanks
Stephen C

I ran into the same issue and I think the valueForPasteboardType family of methods are broken and always return NSData.
Here is my solution:
NSArray * lArrayFromPasteBoard = [pPasteBoard valueForPasteboardType:#"com.my.custom.type"];
if ([lArrayFromPasteBoard isKindOf:[NSData class]])
{
lArrayFromPasteBoard = [[NSPropertyListSerialization propertyListWithData:(NSData*)lArrayFromPasteBoard options:0 format:0 error:0];
}
hopefully this will make it so the code in the if won't get called anymore once apple fixes their bug

As of iOS 8.3, UIPasteboard still has this bug. I wrote an extension for UIPasteboard to handle this:
extension UIPasteboard {
func arrayForPasteboardType(pasteboardType: String) -> NSArray? {
switch valueForPasteboardType(pasteboardType) {
case let array as NSArray:
return array
case let data as NSData:
if let array = NSPropertyListSerialization.propertyListWithData(data, options: 0, format: nil, error: nil) as? NSArray {
return array
}
default:
break
}
return nil
}
}

Related

How to pass optional<vector<optional<uuid>>> from C++ to 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?

Can Apple Watch workout metadata use JSON strings?

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
}

Swift thinks my string is an MDL material

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.

iOS-NSArray filter having condition

I'm new here!
I have an NSArray like this ("file01_header","file01_body", "file01_xxx", ... ,"file02_header", ...).
I filter it and get 2 distinct NSArrays:
Array_header ("file01_header","file02_header",...)
Array_body
("file01_body", "file02_body", ...)
now I have to subtract from Array_header all the items that have a corresponding one in Array_body, because not all items have a fileNN_body for the fileNN_header in the original array, and I have to give the Array_header as output with only items that has no corresponding body.
How can I do this?
Thanks!!
If you're using swift you can filter pretty easily using a closure and some minor string-fu.
Here's an example:
let funkyArray = ["file01_header","file01_body", "file01_xxx", "file02_header","file03_xxx"]
let headerFilterClosure : (String) -> Bool = {fileName in
if fileName.containsString("header") {
let fileBase = fileName.componentsSeparatedByString("_")[0]
let fileBodyName = "\(fileBase)_body"
if funkyArray.contains(fileBodyName) {
return true
}
}
return false
}
funkyArray.filter(headerFilterClosure)
I think a better long term solution would be to not have such a funky array and use a data structure or at least a tuple with options to manage your file info.
Best,
Josh

iOS How to Store C++ Object into a NSArray used as a tableview's datasource

Recently, I encountered a problem: in my tableview, I retrieve data from web request, after received response from the network, I need to decode the data and store the data in a C++ object.
However, the tableview datasource, NSArray cannot store C++ object.
I have tried some solutions, like using CFMutableArrayRef or save the C++ object pointer in a NSValue and then put the NSValue into the NSArray. But when I called [tableview reloadData],the data in the CFMutableArray or the NSValue don't have the right data.
Could anybody help me with this problem? Thanks!
Here are some code:
std::list<ImageTextContent> msgs = body->getMsgs();
std::list<ImageTextContent>::iterator itmsgs0;
for(itmsgs0 = msgs.begin();itmsgs0 != msgs.end();itmsgs0++)
{
ImageTextContent *tmpmsgs = &(*itmsgs0);
CFArrayAppendValue(_dataSourceRef, tmpmsgs);
}
and
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ImageTextContent *itc = (ImageTextContent *)CFArrayGetValueAtIndex(_dataSourceRef, 0);
NSString *title = [NSString stringWithCString:(itc->getSubmitTime()).c_str() encoding:NSUTF8StringEncoding];
cell.contentLabel.text = title;
....
}
in cellForRowAtIndexPath method I can't get the right data, why?
The pointers in your _dataSourceRef array become invalid as soon as the lifetime of msgs ends.
Dereferencing any of them is undefined at that point and anything can happen.
(Very common is that the data has been overwritten and you just find some random stuff there.)
Either make getMsgs return a list of pointers, or dynamically allocate a copy the data when you populate the array, like this:
for(itmsgs0 = msgs.begin();itmsgs0 != msgs.end();itmsgs0++)
{
CFArrayAppendValue(_dataSourceRef, new ImageTextContent(*itmsgs0));
}