Using custom class in XPC protocol - xcode

I’m attempting to write an XPC service using my own type on the withReply signature. The type/class has Xcode’s “target membership” of both the main app and the XPC service. However I am getting incompatible reply block signature in the debug output even though the same class is being used in the withReply signature however the Xcode target differs as I will explain below.
Note: This is being done in Swift using this project to get me started. Except there they use NSData instead of a custom type.
Details
For the purposes of this question I’ll use the following as an example
Custom class - Tweet - This class conforms to the NSSecureCoding protocol so that it can be passed between the main app and the XPC service
XPC Protocol - TweetTransfer with one method required func take(_ count: Int, withReply: ((Tweet) -> Void))
and then all the usual XPC boilerplate where I export an object conforming to TweetTransfer. The XPC service appears to launch but then transfer between it and the main app fails with
XPCWorker[11069:402719] <NSXPCConnection: 0x61800010e220> connection from pid 11066 received an undecodable message
The full message is below[1] but the only difference between the “wire” and “local” is that argument one is
wire - _TtC17MainApp5Tweet
local - _TtC23XPCWorker5Tweet
Where the Xcode target is different. Is that enough to throw it off? How then do I share code between an app and it's XPC service?
[1] Full error text
<NSXPCConnection: 0x61800010e220> connection from pid 11066 received an undecodable message (incompatible reply block signature (wire: <NSMethodSignature: 0x618000074ec0>
number of arguments = 2
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (#) '#?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 1: -------- -------- -------- --------
type encoding (#) '#"_TtC17MainApp5Tweet"'
flags {isObject}
modifiers {}
frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
class '_TtC17MainApp5Tweet'
vs local: <NSMethodSignature: 0x610000074740>
number of arguments = 2
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (#) '#?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 1: -------- -------- -------- --------
type encoding (#) '#"_TtC23XPCWorker5Tweet"'
flags {isObject}
modifiers {}
frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
class '_TtC23XPCWorker5Tweet'
)
Update
Some more info regarding the protocol, remoteObjectProxy connection and Tweet object. This is the protocol used for the XPC calls:
#objc(TweetTransfer)
protocol TweetTransfer {
func take(_ count: Int, withReply: replyType)
}
typealias replyType = ((Tweet) -> Void)
I'm using a type alias for convenience. And then the Tweet object is very simple and just for testing (although somewhat complicated by supporting NSSecureCoding):
final class Tweet: NSObject, NSSecureCoding {
var name: String
var text: String
static var supportsSecureCoding = true
init(name: String, text: String) {
self.name = name
self.text = text
}
init?(coder aDecoder: NSCoder) {
guard let name = aDecoder.decodeObject(forKey: "name") as? String else {
fatalError("Could not deserialise name!")
}
guard let text = aDecoder.decodeObject(forKey: "text") as? String else {
fatalError("Could not deseralise text!")
}
self.name = name
self.text = text
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(text, forKey: "text")
}
}
and finally the point at which we call the remoteObjectProxy
guard let loader = workerConnection.remoteObjectProxyWithErrorHandler(handler) as? TweetTransfer else {
fatalError("Could not map worker to TweetTransfer protocol!")
}
var tweets = [Tweet]()
loader.take(1) { tweet in
tweets.append(tweet)
}

The full message is below but the only difference between the “wire” and “local” is that argument one is
wire - _TtC17MainApp5Tweet
local - _TtC23XPCWorker5Tweet
Where the Xcode target is different. Is that enough to throw it off? How then do I share code between an app and it's XPC service?
That is indeed enough to throw it off. Swift's namespacing makes the archived object appear as a different class. You can disable name spacing by declaring your Tweet object with;
#objc(Tweet) class Tweet: NSObject, NSSecureCoding { ... }
The name in #objc(name) is often presented as a way to present a different name in objc vs Swift, but it also has the effect of disabling Swift's name spacing.
From Using Swift with Cocoa and Objective-C
When you use the #objc(name) attribute on a Swift class, the class is made available in Objective-C without any namespacing. As a result, this attribute can also be useful when migrating an archivable Objective-C class to Swift. Because archived objects store the name of their class in the archive, you should use the #objc(name) attribute to specify the same name as your Objective-C class so that older archives can be unarchived by your new Swift class.
Another alternative is to move your custom object to a Framework. That framework target then becomes the namespace, and both the XPC and App would refer to the same namespace/class in the framework.

Related

bpf_prog_test_run() causes unexpected packet data

I try to perform a test run for an XDP BPF program. The BPF program uses the bpf_xdp_adjust_meta() helper, to adjust the meta data.
I tried:
to run bpf_prog_test_run()
to run bpf_prog_test_run_xattr()
1. bpf_prog_test_run()
(The first time I tried my bpf program's debug messages told me that adjusting the data_meta field failed.) Now it can adjust the data_meta, but the iph.ihl field is apparently not set to 5.
2. bpf_prog_test_xattr()
This always returns -1, so something failed.
The Code
packet:
struct ipv4_packet pkt_v4 = {
.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
.iph.ihl = 5,
.iph.daddr = __bpf_constant_htonl(33554442),
.iph.saddr = __bpf_constant_htonl(50331658),
.iph.protocol = IPPROTO_TCP,
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
.tcp.urg_ptr = 123,
.tcp.doff = 5,
};
test attribute:
__u32 size, retval, duration;
char data_out[128];
struct xdp_md ctx_in, ctx_out;
struct bpf_prog_test_run_attr test_attr = {
.prog_fd = prog_fd,
.repeat = 100,
.data_in = &pkt_v4,
.data_size_in = sizeof(&pkt_v4),
.data_out = &data_out,
.data_size_out = sizeof(data_out),
.ctx_in = &ctx_in,
.ctx_size_in = sizeof(ctx_in),
.ctx_out = &ctx_out,
.ctx_size_out = sizeof(ctx_out),
.retval = &retval,
.duration = &duration,
};
test execution:
bpf_prog_test_run(main_prog_fd, 1, &pkt_v4, sizeof(pkt_v4), &data_out, &size, &retval, &duration) -> iph.ihl field is 0.
bpf_prog_test_run_xattr(&test_attr) -> returns -1.
Note
The program was already successfully attached to the hook point of a real network interface and ran as intended. I just replaced the code that attaches the program to the hook point with the above code for testing.
The struct ipv4_packet pkt_v4 was not packed.
When I replace __packed with __attribute__ ((__packed__)) it works.
For information what happens without packing, see for example this question.
Basically the compiler adds padding bytes which leads to the fields in the packet being in different places than expected.

Xcode 8.1 swift 3 take forever to compile this code

I have this class in a project which previously use swift 2.3. When i migrated the project to swift 3, xcode took forever to compile and i saw it stuck at this class. I can not build the whole project because of this class. Is there a way to modify this class so the project can be built, it took Xcode forever to compile this piece of code. If i removed several properties from MyClass, Xcode will quickly compile again. Anyone has any idea on how to solve this problem?
import Foundation
class MyClass: NSObject {
var id: String = ""
var uid: String = ""
var uname: String = ""
var fname: String = ""
var txt: String = ""
var hay: Float = 0
var flag = false
var long: Double = 0
var lat: Double = 0
var altitude: Double = 0
var course: Double = 0
var speed: Double = 0
var lname: String = ""
var city: String = ""
var country: String = ""
var sublocal: String = ""
var subarea: String = ""
var thumb: String = ""
var trash = false
var date: Double = 0
var updated: Double = 0
var furl: String = ""
func toAnyObject() -> Any {
return [
"id": id,
"uid": uid,
"uname": uname,
"fname": fname,
"txt": txt,
"hay": hay,
"flag": flag,
"long": long,
"lat": lat,
"altitude": altitude,
"course": course,
"speed": speed,
"lname": lname,
"city": city,
"country": country,
"sublocal": sublocal,
"trash": trash,
"subarea": subarea,
"thumb": thumb,
"date": date,
"updated": updated,
"furl": furl
]
}
}
Rewrite without the big dictionary literal. So:
func toAnyObject() -> Any {
var d = [String:Any]()
d["id"] = id
d["uid"] = uid
// ... and so on ...
return d
}
If you're not doing so already adding a -Xfrontend -debug-time-function-bodies compiler flag to your project will list the time required to compile each function. That can be a useful way to identify what exactly is slow in your build. (See http://irace.me/swift-profiling or https://thatthinginswift.com/debug-long-compile-times-swift/).
In your case the compiler is probably struggling to determine the type of your dictionary literal. Looking at each key and value and then trying to find the most appropriate common type for all of them. If you specified a type then I expect the compiler will only need to verify that your literal matches that type and compile much more quickly:
let result: [String: Any] = ["id": id, ...]
return result
Sometimes the compiler slows down when you do implicit typing. If you Explicitly add the type information then the compiler will not need to calculate it. I can see that the properties of your class mostly have type information but not all of them. In your toAnyObject method, it seems like you want your object represented as a dictionary, yet you are converting it to type Any.
You are making a dictionary literal and offering no type information. Explicitly casting with "as" can help a lot.
When you convert something to type Any, Objective-C interprets that as id. Normally a swift dictionary would be bridged to an NSDictionary for Objective-c, but you are forcing it to be of type Any. What reason would the compiler have to bridge this to an NSDictionary? It probably boxes it since it thinks it is a struct and objective-c can't use swift structs.
Try to not confuse the compiler.

Expected Declaration Swift (Sprite Kit)

I'm using Xcode 7 with swift and when I type
class Block {
var Block = SKShapeNode(circleOfRadius: 15)
Block.fillColor = SKColor.redColor() //Error Here
Block.physicsBody = SKPhysicsBody(circleOfRadius: 15)
Block.physicsBody?.affectedByGravity = true
Block.physicsBody?.restitution = 0
Block.physicsbody?.LinearDamping = 0
self.addChild(Block)
It gives me an error that says "expected declaration" (on the line with the comment) and I don't know why
There are some errors:
You have a class and are trying to change class properties outside the scope of a method or an initializer.
The line Block.physicsbody?.LinearDamping = 0 should be Block.physicsBody?.linearDamping = 0; case-sensitivity.
You name your SKShapeNode instance as Block, the same name as your class. By naming convention, class (type) names begin with Capital letters, whereas class properties use small letters in their names.
With these three fixed, we can proceed to looking at your scene.
With help from Leo Dabus (thanks!), we should have enough to set you up with a minimal working example (using your code) of the SKScene:
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let block = SKShapeNode(circleOfRadius: 15)
// you will also need to set your node initial position
// if you would like your red circle to fall from the middle of the top of your scene you need to use the scene frame midX and maxY (not the view frame). the scene it is not necessarily the same size of your view)
block.position = CGPoint(x: scene!.frame.midX, y: scene!.frame.maxY)
block.fillColor = SKColor.redColor()
block.physicsBody = SKPhysicsBody(circleOfRadius: 15)
block.physicsBody?.affectedByGravity = true
block.physicsBody?.restitution = 0
block.physicsBody?.linearDamping = 0
self.addChild(block)
}
// ...
}

Using SecKeychainCreate from Swift

I am writing an OS X app that should maintain a custom Keychain, I am trying to use the Security framework's API to create the Keychain, however, I can't seem to get it to compile under Swift.
Here's what I have, assume that path contains a path to a potentially existing Keychain:
let pathName = (path as NSString).UTF8String
var keychain: Unmanaged<SecKeychain>?
var status = withUnsafeMutablePointer(&keychain) { pointer in
SecKeychainOpen(pathName, pointer)
}
if status != errSecSuccess {
status = withUnsafeMutablePointer(&keychain) { pointer in
SecKeychainCreate(pathName, UInt32(0), nil, false, nil, pointer)
}
}
The compiler is complaining about the types in the SecKeychainCreate call, however, I fail to understand what am I doing wrong.
Cannot invoke 'withUnsafeMutablePointer' with an argument list of type '(inout Unmanaged<SecKeychain>?, (_) -> _)'
If I modify the second closure slightly, I get this compiler error:
Cannot invoke 'SecKeychainCreate' with an argument list of type '(UnsafePointer<Int8>, UInt32, nil, Bool, nil, (UnsafeMutablePointer<Unmanaged<SecKeychain>?>))'
I appreciate all suggestions.
The promptUser parameter of SecKeychainCreate() has the type
Boolean, which is a "Mac OS historic type" and an alias to UInt8,
so it is different from the Swift Bool in Swift 1.2.
(Compare Type 'Boolean' does not conform to protocol 'BooleanType' for a similar issue.)
This means that you have to
pass Boolean(0) instead of false:
SecKeychainCreate(pathName, UInt32(0), nil, Boolean(0), nil, pointer)
Additional remarks:
withUnsafeMutablePointer() is not needed, you can pass &keychain
to the keychain functions.
(path as NSString).UTF8String is not needed, you can pass a Swift
string to a C function expecting a const char * parameter,
compare String value to UnsafePointer<UInt8> function parameter behavior.
Passing nil as password to SecKeychainCreate() is only allowed
if promptUser is TRUE, otherwise it causes a
"parameter error (-50)".
SecKeychainOpen() succeeds even if the keychain file does not
exists. According to the documentation, you have to check
SecKeychainGetStatus(). Alternatively, you can try to create
the keychain file first, as for example in Open Local Items Keychain?.
Together:
let path = "/path/to/my.keychain"
var keychain: Unmanaged<SecKeychain>?
var status = SecKeychainCreate(path, 0, "", Boolean(0), nil, &keychain)
if status == OSStatus(errSecDuplicateKeychain) {
status = SecKeychainOpen(path, &keychain)
}
As of Swift 2 / Xcode 7 beta 5, the Mac type Boolean is mapped
to Swift as Bool, and the key chain functions do no longer return
unmanaged objects:
let path = "/path/to/my.keychain"
var keychain: SecKeychain?
var status = SecKeychainCreate(path, 0, "", false, nil, &keychain)
if status == OSStatus(errSecDuplicateKeychain) {
status = SecKeychainOpen(path, &keychain)
}

Differences between Playground and Project

In the course of answering another question, I came across a weird bug in Playground. I have the following code to test if an object is an Array, Dictionary or a Set:
import Foundation
func isCollectionType(value : AnyObject) -> Bool {
let object = value as! NSObject
return object.isKindOfClass(NSArray)
|| object.isKindOfClass(NSDictionary)
|| object.isKindOfClass(NSSet)
}
var arrayOfInt = [1, 2, 3]
var dictionary = ["name": "john", "age": "30"]
var anInt = 42
var aString = "Hello world"
println(isCollectionType(arrayOfInt)) // true
println(isCollectionType(dictionary)) // true
println(isCollectionType(anInt)) // false
println(isCollectionType(aString)) // false
The code worked as expected when I put it into a Swift project or running it from the command line. However Playground wouldn't compile and give me the following error on the downcast to NSObject:
Playground execution failed: Execution was interrupted, reason: EXC_BAD_ACCESS (code=2, address=0x7fb1d0f77fe8).
* thread #1: tid = 0x298023, 0x00007fb1d0f77fe8, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7fb1d0f77fe8)
* frame #0: 0x00007fb1d0f77fe8
frame #1: 0x000000010ba46e12 libswiftCore.dylib`Swift._EmptyArrayStorage._withVerbatimBridgedUnsafeBuffer (Swift._EmptyArrayStorage)<A>((Swift.UnsafeBufferPointer<Swift.AnyObject>) -> A) -> Swift.Optional<A> + 50
The build platform was OS X in all three cases. Does anyone know how to get Playground to play along?
Xcode 6.3.2. Swift 1.2. OS X 10.10.3 Yosemite
Not really the cause of that bug (it does look weird) but...
You will need to use optional chaining since value can be AnyObject:
import Foundation
func isCollectionType(value : AnyObject) -> Bool {
if let object = value as? NSObject {
return object.isKindOfClass(NSArray)
|| object.isKindOfClass(NSDictionary)
|| object.isKindOfClass(NSSet)
}
return false
}
var arrayOfInt = [1, 2, 3]
var dictionary = ["name": "john", "age": "30"]
var anInt = 42
var aString = "Hello world"
isCollectionType(arrayOfInt)
isCollectionType(dictionary)
isCollectionType(anInt)
isCollectionType(aString)
Also worth noting, NSArray and Array are different things:
NSArray is an immutable class:
#interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>
whilst Array is a struct:
struct Array<T> : MutableCollectionType, Sliceable, _DestructorSafeContainer
With this in mind it might be surprising that isCollectionType(arrayOfInt) returns true - but there is a conversion happening.

Resources