How to transfer an NSObject pointer to selector in Object C? - xcode

I want to transfer an Object pointer as a parameter to me selector, however, it will not allow me to do that, is there anyway to do that? or is there any workaround here?
I have two Classes, A Class will do something and then the result feed back to B class.
what I am doing now in A instance:
MyObject *b;
[b addTarget:self withSelector:#selector(dosomething:) ];
-(void)dosomething:(NSString **)string
{
*string="some thing is implemented";
}
B instance:
{
NSString *mystring;
[a performSelector:dosomething withObject:&mystring];
}
Thanks.

You can only pass a valid Objective-C object to -performSelector:withObject:.
You have three options.
Pass a mutable string:
- (void)doSomething:(NSMutableString*)foo
{
[foo setString:#"Hello"];
}
Return a string:
- (NSString*)doSomething
{
return #"Hello";
}
Pass an NSValue that contains a NSString**:
- (void)doSomething:(NSValue*)value
{
NSString** stringPointer = (NSString**)[value pointerValue];
*stringPointer = #"Hello";
}

Related

Passing Dictionary to Watch

I'm trying to pass data from iPhone -> Watch via Watch Connectivity using background transfer via Application Context method.
iPhone TableViewController
private func configureWCSession() {
session?.delegate = self;
session?.activateSession()
print("Configured WC Session")
}
func getParsePassData () {
let gmtTime = NSDate()
// Query Parse
let query = PFQuery(className: "data")
query.whereKey("dateGame", greaterThanOrEqualTo: gmtTime)
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let objectsFromParse = objects as? [PFObject]{
for MatchupObject in objectsFromParse
{
let matchupDict = ["matchupSaved" : MatchupObject]
do {
try self.session?.updateApplicationContext(matchupDict)
print("getParsePassData iPhone")
} catch {
print("error")
}
}
}
}
}
}
I'm getting error twice printed in the log (I have two matchups in Parse so maybe it knows there's two objects and thats why its throwing two errors too?):
Configured WC Session
error
error
So I haven't even gotten to the point where I can print it in the Watch app to see if the matchups passed correctly.
Watch InterfaceController:
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
let matchupWatch = applicationContext["matchupSaved"] as? String
print("Matchups: %#", matchupWatch)
}
Any ideas? Will post any extra code that you need. Thanks!
EDIT 1:
Per EridB answer, I tried adding encoding into getParsePassData
func getParsePassData () {
let gmtTime = NSDate()
// Query Parse
let query = PFQuery(className: "data")
query.whereKey("dateGame", greaterThanOrEqualTo: gmtTime)
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let objectsFromParse = objects as? [PFObject]{
for MatchupObject in objectsFromParse
{
let data = NSKeyedArchiver.archivedDataWithRootObject(MatchupObject)
let matchupDict = ["matchupSaved" : data]
do {
try self.session?.updateApplicationContext(matchupDict)
print("getParsePassData iPhone")
} catch {
print("error")
}
}
}
}
}
}
But get this in the log:
-[PFObject encodeWithCoder:]: unrecognized selector sent to instance 0x7fbe80d43f30
*** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
EDIT 2:
Per EridB answer, I also tried just pasting the function into my code:
func sendObjectToWatch(object: NSObject) {
//Archiving
let data = NSKeyedArchiver.archivedDataWithRootObject(MatchupObject)
//Putting it in the dictionary
let matchupDict = ["matchupSaved" : data]
//Send the matchupDict via WCSession
self.session?.updateApplicationContext(matchupDict)
}
But get this error on the first line of the function:
"Use of unresolved identifer MatchupObject"
I'm sure I must not be understanding how to use EridB's answer correctly.
EDIT 3:
NSCoder methods:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
//super.init(coder: aDecoder)
configureWCSession()
// Configure the PFQueryTableView
self.parseClassName = "data"
self.textKey = "matchup"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
Error
You are getting that error, because you are putting a NSObject (MatchupObject) which does not conform to NSCoding inside the dictionary that you are going to pass.
From Apple Docs
For most types of transfers, you provide an NSDictionary object with
the data you want to send. The keys and values of your dictionary must
all be property list types, because the data must be serialized and
sent wirelessly. (If you need to include types that are not property
list types, package them in an NSData object or write them to a file
before sending them.) In addition, the dictionaries you send should be
compact and contain only the data you really need. Keeping your
dictionaries small ensures that they are transmitted quickly and do
not consume too much power on both devices.
Details
You need to archive your NSObject's to NSData and then put it in the NSDictionary. If you archive a NSObject which does not conform to NSCoding, the NSData will be nil.
This example greatly shows how to conform a NSObject to NSCoding, and if you implement these things then you just follow the code below:
//Send the dictionary to the watch
func sendObjectToWatch(object: NSObject) {
//Archiving
let data = NSKeyedArchiver.archivedDataWithRootObject(MatchupObject)
//Putting it in the dictionary
let matchupDict = ["matchupSaved" : data]
//Send the matchupDict via WCSession
self.session?.updateApplicationContext(matchupDict)
}
//When receiving object from the other side unarchive it and get the object back
func objectFromData(dictionary: NSDictionary) -> MatchupObject {
//Load the archived object from received dictionary
let data = dictionary["matchupSaved"]
//Deserialize data to MatchupObject
let matchUpObject = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! MatchupObject
return matchUpObject
}
Since you are using Parse, modifying an object maybe cannot be done (I haven't used Parse in a while, so IDK for sure), but from their forum I found this question: https://parse.com/questions/is-there-a-way-to-serialize-a-parse-object-to-a-plain-string-or-a-json-string which can help you solve this problem easier than it looks above :)

How to include all keys in single query to Parse object

var query = new Parse.Query("Order");
query.equalTo("objectId", orederId);
query.include("price");
...
query.include("keyName");
Is it possible in Parse to include all existing object's keys in single query if we don't know keys names?
Assume that object was created dynamically and there is no info about it's keys names, but we need to get this object with all included pointers.
Create a subclass for your object and override the query method like this. Put all includeKeys where the comment says. Use the class method initObject to create and initialize your objects.
#implementation MyPFObject
#dynamic objectUUID;
// Other fields
+ (MyPFObject*) initObject
{
MyPFObject* obj = [self object];
[obj tlObjectInit];
// Other initializations
return obj;
}
+ (NSString*) parseClassName
{
return #"MyPFObject";
}
+ (PFQuery*) query
{
PFQuery* query = [PFQuery queryWithClassName: [self parseClassName]];
// Add includeKeys here
return query;
}
#end

How can I override the description property of NSObject when subclassing in Swift?

I am subclassing NSObject in order to have an ordered collection that is accessible to Cocoa Bindings. My class looks more or less like this:
public class OrderedCollection<Tk: Hashable, Tv> : NSObject {
var keys: Array<Tk> = []
var values: Dictionary<Tk,Tv> = [:]
override init() {
super.init()
}
// Subscript methods go here
override public var description: String {
var result = "{\n"
for i in 0..<self.count {
result += "[\(i)]: \(self.keys[i]) => \(self[i]!)\n"
}
result += "}"
return result
}
}
It doesn't compile. The error says: '#objc' getter for non-'#objc' property.
Is there a way of making the getter non-'#objc' as it were? I don't need the property to be accessible from Objective-C...
It seems the answer was in the comments of an entirely different question. https://stackoverflow.com/a/26688572/4180258
Essentially, there is a bit of an ugly workaround:
class BaseNSObjectWithDescriptionFix: NSObject {
func makeDescription() -> String {
return super.description
}
override var description: String {
return makeDescription()
}
}
Now you just use BaseNSObjectWithDescriptionFix instead of NSObject and override makeDescription as you like.
In my case, I didn't need it because for my purposes I could use [String] and [String:AnyObject], but this may be of some use to someone in the future.
To override the description property of NSObject when subclassing in Swift, with two generic types like you have and keeping your class public, you just need this:
public class OrderedCollection<Tk: Hashable, Tv>: NSObject {
override public var description: String {
return "hello"
}
}
You could also, instead, conform to the CustomStringConvertible protocol (formerly, pre-Swift 2, known as Printable) and forget NSObject, like so:
public class OrderedCollection<Tk: Hashable, Tv>: CustomStringConvertible {
public var description: String {
return "hello"
}
}
In other words, generics don't really change anything for this case. (Not sure if things were different in an earlier version of Swift...)
The content of description I leave to you (e.g. you don't have a count property above, so I'm guessing you omitted more code than just subscript methods).

How do I compare an NSNumber to a swift enumeration value?

I'm trying to do a simple comparison of a value received via an NSNotification, and an enumeration value. I have something that works, but I can't believe that this is the right way to be doing this. Basically the solution I ended up with was converting the NSNumber to an Int, and also getting the enum value's rawValue, wrapping it in an NSNumber and then getting the integerValue of that.
Everything else I tried resulted in compiler errors about not being able to convert between Uint 8 and Int or something similar.
observer = NSNotificationCenter.defaultCenter().addObserverForName(AVAudioSessionRouteChangeNotification, object: nil, queue: mainQueue) { notification in
println(AVAudioSessionRouteChangeReason.NewDeviceAvailable.toRaw())
if let reason = notification.userInfo[AVAudioSessionRouteChangeReasonKey!] as? NSNumber {
if (reason.integerValue == NSNumber(unsignedLong:AVAudioSessionRouteChangeReason.NewDeviceAvailable.toRaw()).integerValue) {
self.headphoneJackedIn = true;
} else if (reason.integerValue == NSNumber(unsignedLong:AVAudioSessionRouteChangeReason.OldDeviceUnavailable.toRaw()).integerValue) {
self.headphoneJackedIn = false;
}
self.updateHeadphoneJackLabel()
}
}
This should work:
if reason.integerValue == Int(AVAudioSessionRouteChangeReason.NewDeviceAvailable.rawValue) {
// ...
}
Remark: The AVAudioSessionRouteChangeReason enumeration uses UInt as raw value type, so
one could expect that this works without an explicit conversion:
if reason.unsignedIntegerValue == AVAudioSessionRouteChangeReason.NewDeviceAvailable.rawValue {
}
However, as of Xcode 6 beta 4, NSUInteger is mapped to Swift's Int in OS X and iOS
system frameworks, which means that both integerValue and unsignedIntegerValue return an Int,
and an explicit conversion is needed in any case.
Alternatively, you could create an enumeration value from the number (as already suggested by #ColinE
in a comment) and then use switch case:
if let r = AVAudioSessionRouteChangeReason(rawValue: UInt(reason.integerValue)) {
switch r {
case .NewDeviceAvailable:
// ...
case .OldDeviceUnavailable:
// ...
default:
// ...
}
} else {
println ("Other reason")
}
For the same reason as above, an explicit UInt conversion is necessary here as well.

Copy/Paste of a custom managed object in core data

I am having a very hard time getting copy/paste to work on my custom managed object Person. The object contains properties and relationships. The object should provide the objectID. I intend to implement pasting generating a new object and then filling in the information from the copied Person.
Copying the objectID probably does work. I am certain that pasting does not work. I have the following methods implemented in my Person class, in an attempt to copy/paste an object:
#pragma mark --- Copy functionality
-(id)pasteboardPropertyListForType:(NSString *)type
{
if ( [type isEqualToString:#"my.company.person"])
{
NSManagedObjectID *oid = self.objectID;
NSURL *uidURL = [oid URIRepresentation];
return [uidURL absoluteString];
}
return nil;
}
-(NSArray *)writableTypesForPasteboard:(NSPasteboard *)pasteboard
{
return #[#"my.company.person"];
}
+ (NSPasteboardWritingOptions)writingOptionsForType:(NSString *)type pasteboard:(NSPasteboard *)pasteboard
{
if ( [type isEqualToString:#"my.company.person"])
{
return NSPasteboardWritingPromised;
}
return nil;
}
and to do the pasting:
#pragma mark --- Paste functionality
+(NSArray *)readableTypesForPasteboard:(NSPasteboard *)pasteboard
{
return #[#"my.company.person"];
}
+ (NSPasteboardReadingOptions)readingOptionsForType:(NSString *)type pasteboard:(NSPasteboard *)pasteboard
{
if ( [type isEqualToString:#"my.company.person"])
{
return NSPasteboardReadingAsString;
}
return nil;
}
- (id)initWithPasteboardPropertyList:(id)propertyList ofType:(NSString *)type
{
if ( [type isEqualToString:#"my.company.person"])
{
...
}
return nil;
}
How should I proceed here? I am at a loss, reading many stackoverflow Q&A's (e.g. Peter Hosey's great answer to NSPasteboard and simple custom data), as well as the Apple docs, still have me stumped on this one.
Have you already seen this:
Using Managed Objects?
I'm not sure what you mean about 'I need to return a Person object but I don't have a managed object context.' Your context is your scratch pad to create things.

Resources