1

I am having a bit of an access control conundrum when using structs in place of classes to achieve a more protocol oriented programming approach.

I am receiving different message types over the network, which in their raw form are simply an array of bytes.

So I start with a protocol. Note how the rawBytes is marked only as { get } so callers can't directly manipulate the raw bytes:

protocol NetworkDataRequest {
     var rawBytes: [UInt8] { get }
}

I'm trying to be thread safe and use all the value type, protocol oriented goodness of Swift, so I now create my different message types using structs instead of classes, and adopt the protocol.

struct FileRequest: NetworkDataRequest {
     private(set) var rawBytes: [UInt8]
}

struct ConnectionRequest: NetworkDataRequest {
     private(set) var rawBytes: [UInt8]
}

All the different message types share a similar structure for the first 10 bytes, for example:

  • byte[0] = permissionsByte
  • byte[1] = connectionTypeByte
  • etc...

Since I'm using structs I don't have inheritance. But I still need some way to have similar behavior between message types. OK, so I use a protocol extension:

 extension NetworkDataRequest {

      var permissionsByte: UInt8 {
          get { return bytes[0] }
          set { bytes[0] = newValue } //<-- Nope!
      }

 }

But oops! The rawBytes is inaccessible:

Cannot assign through subscript: 'rawBytes' is a get-only property

Is there any way around this? Otherwise all my structs are going to have a lot of boilerplate code (sucks for me), or I'm going to have to open up the rawBytes (very bad)

struct FileRequest: NetworkDataRequest {

     private(set) var rawBytes: [UInt8]

      var permissionsByte: UInt8 {
          get { return bytes[0] }
          set { bytes[0] = newValue }
      }


      var connectionTypeByte: UInt8 {
          get { return bytes[1] }
          set { bytes[1] = newValue } 
      }

      ///etc...

}
MH175
  • 2,234
  • 1
  • 19
  • 35

2 Answers2

1

How about something like this?

public protocol NetworkDataRequest {
     var rawBytes: [UInt8] { get }
}

private protocol NetworkDataRequestPrivate: NetworkDataRequest {
     var rawBytes: [UInt8] { get set }
}

extension NetworkDataRequest {
    var permissionsByte: UInt8 {
        get { return rawBytes[0] }
    }
}

extension NetworkDataRequestPrivate {
    var permissionsByte: UInt8 {
        get { return (self as NetworkDataRequest).permissionsByte }
        set { rawBytes[0] = newValue }
    }
}

public struct FileRequest: NetworkDataRequestPrivate {
    fileprivate(set) public var rawBytes: [UInt8]
}

public struct ConnectionRequest: NetworkDataRequestPrivate {
    fileprivate(set) public var rawBytes: [UInt8]
}
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • Interesting!. The only issue I see with this is that now I'm stuck in a single source file. Some of these request types have huge dictionaries associated with them taking up many lines of code. i.e. when byte[9] = 100 it's "Hello", when byte[9] = 101 it's "Goodbye" etc. – MH175 May 01 '17 at 04:43
  • @MH175 That'll be fixed in Swift 4. `private` will extend access to extensions within the same module https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md – Alexander May 01 '17 at 04:47
  • Within the same source file, not the same module. – Martin R May 01 '17 at 05:16
  • @MartinR Oops, I remembered it wrong. Never mind then :p – Alexander May 01 '17 at 05:18
0

If the protocol and the protocol extension is defined in the same file then use the fileprivate instead of private

fileprivate allows code from the same file to access it but outside the file it is inaccessible.

Refer - https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html

user1046037
  • 16,755
  • 12
  • 92
  • 138