Search code examples
swiftnsdata

How can I exchange various type's data through NSData?


I'm creating a bluetooth match game application. I'm using func session(session: MCSession, didReceiveData data: NSData, fromPeer peerID: MCPeerID) on MCSessionDelegate.

I want to exchange various type's data, like GameStatusClass, PlayerClass, EnemyClass, CGPoint etc, case by case. Then this is my code. I think, my code is not good because unarchiveObjectWithData is called twice.

Do you have some idea to write simply or nice sample code?

enum ModelType {
    case A
    case B
}

class BasicModel: NSObject {
    var modelType : ModelType?
}
class ModelA: BasicModel {
    var x = 100
    override init() {
        super.init()
        self.modelType = ModelType.A
    }
}
class ModelB: BasicModel {
    var y = "test"
    override init() {
        super.init()
        self.modelType = ModelType.B
    }
}


class NearbyManager : NSObject, MCSessionDelegate {
    func session(session: MCSession, didReceiveData data: NSData,
        fromPeer peerID: MCPeerID)  {
            dispatch_async(dispatch_get_main_queue()) {
                let tmp = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! BasicModel
                switch tmp.modelType! {
                case ModelType.A:
                    let a = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! ModelA
                    self.funcA(a)
                case ModelType.B:
                    let b = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! ModelB
                    self.funcB(b)
                }
            }
    }
    func funcA(data: ModelA) {
        print(data.x)
    }
    func funcB(data: ModelB) {
        print(data.y)
    }
}

Solution

  • A possible solution:
    Creation:
    • Create a NSData with the model type.
    • Archive your Object to NSData.
    • Append the two data.

    Reading:
    • Read the first octet for getting the type.
    • Unarchive the rest (with offset).

    In Objective-C, but it should be easily translated in Swift (code not tested, but you should get the logic)

    //Sending:

    NSData *typeData = [NSData dataWithBytes:&ModelTypeA length: sizeof(uint_16)];
    NSData *objectData = [NSKeyedArchiver archivedDataWithRootObject:objectA];
    NSMutableData *dataToSend = [[NSMutableData alloc] init];
    [dataToSend appendData:typeData];
    [dataToSend appendData:objectData];
    

    //Reading:

    if ([data length] > 1)
    {
        NSData *typeData = [data subdataWithRange:NSMakeRange(0,1)];
        uint_16 type;
        [typeData getBytes:type];
        NSData *objectData = [data subdataWithRange:NSMakeRange(1, [data length]-1];
        switch (type)
        {
            case ModelTypeA:
            {
                ModelA *a = (ModelA *)[NSKeyedUnarchiver unarchiveObjectWithData:objectData];
                [self func:a];
            }
                break;
            case ModelTypeB:
            {
                ModelB *b = (ModelB *)[NSKeyedUnarchiver unarchiveObjectWithData:objectData];
                [self func:b];
            }
                break;
            default:
                break;
        }
    }
    

    Note: I put the offset to 1, but if you want more and more possible values, you may want to get a bigger offset, but that should be enough kind of object already.