5 Replies
      Latest reply on Dec 21, 2018 5:02 AM by Claude31
      rsharp Level 3 Level 3 (155 points)

        Problem: We have a struct that echoes what our web-based product will send down as JSON.  Unfortunately, it's a large list of separate boolean properties.  The struct in Swift is effectively:

         

        struct SomeStruct : Codable {
            var flagOne : Bool
            var flagTwo : Bool
            ...
            var flagN : Bool
        }

         

        For testing purposes, I'd like to create a instance, but don't want to expose a massive init or have to have calling code manually assign properties.  What I would like to do is to create an enum as follows and then have some simple init() on the struct to basically allow for say "I want an instance with only the following flags set"  where "following flags" is a set/array of the enum:

         

        enum Flag : String {
            case flagOne, flagTwo, ... flagN // The rawValue now matches the struct properties
        }
        
        init(onlyFlags aFlagsArray: [Flag]) {
            // Not sure what to do here.
        }

         

        I haven't found any method such that given a string (e.g. "flagOne"), that I could some how call a "selector" with that name against an instance of the struct.   If such a thing is possible, I could create a few variations of init to do stuff like this (after making the enum CaseIteratable):

         

        init(allFlagsEnabled anEnabledFlag: Bool) {
            for theFlag in Flag.allCases {
                // theFlag.rawValue is the name of the property on the struct
                // But how to call the setter of that property?
            }
        }

         

        I briefly looked at Mirrors, but that seems to be for read-only access.  And, they are effectively value types.  So while you can update a value on a mirror, that won't update the original item (struct).

         

        I belive this may be possible if this isn't a struct but an NSObject and then attempt something with selectors.  But, I'd rather just manually set up the struct's properties as needed at that point.

        • Re: Easily updating a large struct?
          eskimo Apple Staff Apple Staff (11,975 points)

          I think key paths are your best bet here.

          struct SomeStruct {
              var flagOne: Bool = false
              var flagTwo: Bool = false
          }
          
          extension SomeStruct {
              init(onlyFlags keyPaths: [WritableKeyPath<SomeStruct, Bool>]) {
                  var s = SomeStruct()
                  for kp in keyPaths {
                      s[keyPath: kp] = true
                  }
                  self = s
              }
          }
          
          let s1 = SomeStruct(onlyFlags: [\.flagOne])
          print(s1)   // -> SomeStruct(flagOne: true, flagTwo: false)
          let s2 = SomeStruct(onlyFlags: [\.flagTwo])
          print(s2)   // -> SomeStruct(flagOne: false, flagTwo: true)

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: Easily updating a large struct?
              Claude31 Level 8 Level 8 (6,565 points)

              Whow !

               

              But there is something I do not understand.

               

              Why is extension needed ?

               

              This would not work (at least in simulator), even though I thought it should be equivalent.

               

              Sorry, cannot format code, the editor deletes what is inside brackets !

               

              struct SomeStruct {

                  var flagOne: Bool = false

                  var flagTwo: Bool = false

               

                  init(onlyFlags keyPaths: [WritableKeyPath<SomeStruct, Bool>]) {

                      var s = SomeStruct()          // Error here

                      for kp in keyPaths {

                          s[keyPath: kp] = true

                      }

                      self = s

                  }

              }

               

              I get error

              Missing argument for parameter 'onlyFlags' in call

                • Re: Easily updating a large struct?
                  eskimo Apple Staff Apple Staff (11,975 points)

                  Why is extension needed ?

                  Yeah, that’s quite subtle.  If you define a structure without an initialiser, the language synthesises a default one.  You can then define another initialiser in an extension and have that use the default initialiser.

                  In constrast, if you define your initialiser in the structure itself, the language does not synthesise one and thus there’s nothing for your initialiser to call.

                  Tricky.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"

              • Re: Easily updating a large struct?
                rsharp Level 3 Level 3 (155 points)

                Very cool! Thank you, Quinn.