diff --git a/Scripts/ProtoWrappers.py b/Scripts/ProtoWrappers.py index a7beba77b..b07d714d4 100755 --- a/Scripts/ProtoWrappers.py +++ b/Scripts/ProtoWrappers.py @@ -407,7 +407,14 @@ class MessageContext(BaseContext): for field in explict_fields: type_name = field.type_swift_not_optional if field.is_required else field.type_swift writer.add('@objc public let %s: %s' % (field.name_swift, type_name)) - writer.newline() + + if (not field.is_required) and field.rules != 'repeated': + writer.add('@objc public var %s: Bool {' % field.has_accessor_name() ) + writer.push_indent() + writer.add('return proto.%s' % field.has_accessor_name() ) + writer.pop_indent() + writer.add('}') + writer.newline() if len(implict_fields) > 0: for field in implict_fields: diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index f71a6d0d7..f33043799 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -135,7 +135,6 @@ message DataMessage { CUSTOM = 4; } - // @required optional string value = 1; optional Type type = 2; optional string label = 3; @@ -149,7 +148,6 @@ message DataMessage { CUSTOM = 4; } - // @required optional string value = 1; optional Type type = 2; optional string label = 3; diff --git a/SignalServiceKit/src/Devices/OWSGroupsOutputStream.m b/SignalServiceKit/src/Devices/OWSGroupsOutputStream.m index 00459cee3..d02502044 100644 --- a/SignalServiceKit/src/Devices/OWSGroupsOutputStream.m +++ b/SignalServiceKit/src/Devices/OWSGroupsOutputStream.m @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN SSKProtoGroupDetailsBuilder *groupBuilder = [SSKProtoGroupDetailsBuilder new]; [groupBuilder setId:group.groupId]; [groupBuilder setName:group.groupName]; - [groupBuilder setMembersArray:group.groupMemberIds]; + [groupBuilder setMembers:group.groupMemberIds]; #ifdef CONVERSATION_COLORS_ENABLED [groupBuilder setColor:groupThread.conversationColorName]; #endif @@ -38,7 +38,14 @@ NS_ASSUME_NONNULL_BEGIN [avatarBuilder setContentType:OWSMimeTypeImagePng]; avatarPng = UIImagePNGRepresentation(group.groupImage); [avatarBuilder setLength:(uint32_t)avatarPng.length]; - [groupBuilder setAvatarBuilder:avatarBuilder]; + + NSError *error; + SSKProtoGroupDetailsAvatar *_Nullable avatarProto = [avatarBuilder buildAndReturnError:&error]; + if (error || !avatarProto) { + OWSFail(@"%@ could not build protobuf: %@", self.logTag, error); + } else { + [groupBuilder setAvatar:avatarProto]; + } } OWSDisappearingMessagesConfiguration *_Nullable disappearingMessagesConfiguration = @@ -53,7 +60,18 @@ NS_ASSUME_NONNULL_BEGIN [groupBuilder setExpireTimer:0]; } - NSData *groupData = [[groupBuilder build] data]; + NSError *error; + SSKProtoGroupDetails *_Nullable groupProto = [groupBuilder buildAndReturnError:&error]; + if (error || !groupProto) { + OWSFail(@"%@ could not build protobuf: %@", self.logTag, error); + return; + } + NSData *_Nullable groupData = [groupProto serializedDataAndReturnError:&error]; + if (error || !groupData) { + OWSFail(@"%@ could not serialize protobuf: %@", self.logTag, error); + return; + } + uint32_t groupDataLength = (uint32_t)groupData.length; [self.delegateStream writeRawVarint32:groupDataLength]; diff --git a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m index b2ec9a9b2..8ad00a098 100644 --- a/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m +++ b/SignalServiceKit/src/Messages/DeviceSyncing/OWSOutgoingSentMessageTranscript.m @@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN * recipientId is nil when building "sent" sync messages for messages * sent to groups. */ -- (SSKProtoDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId; +- (nullable SSKProtoDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId; @end @@ -58,7 +58,13 @@ NS_ASSUME_NONNULL_BEGIN SSKProtoSyncMessageSentBuilder *sentBuilder = [SSKProtoSyncMessageSentBuilder new]; [sentBuilder setTimestamp:self.message.timestamp]; [sentBuilder setDestination:self.sentRecipientId]; - [sentBuilder setMessage:[self.message buildDataMessage:self.sentRecipientId]]; + + SSKProtoDataMessage *_Nullable dataMessage = [self.message buildDataMessage:self.sentRecipientId]; + if (!dataMessage) { + OWSFail(@"%@ could not build protobuf: %@", self.logTag, error); + return nil; + } + [sentBuilder setMessage:dataMessage]; [sentBuilder setExpirationStartTimestamp:self.message.timestamp]; NSError *error; diff --git a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m index 95df57851..e1065d7e0 100644 --- a/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m +++ b/SignalServiceKit/src/Messages/Interactions/TSOutgoingMessage.m @@ -930,20 +930,43 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt } // recipientId is nil when building "sent" sync messages for messages sent to groups. -- (SSKProtoDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId +- (nullable SSKProtoDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId { OWSAssert(self.thread); SSKProtoDataMessageBuilder *builder = [self dataMessageBuilder]; [builder addLocalProfileKeyIfNecessary:self.thread recipientId:recipientId]; - return [builder build]; + NSError *error; + SSKProtoDataMessage *_Nullable dataProto = [builder buildAndReturnError:&error]; + if (error || !dataProto) { + OWSFail(@"%@ could not build protobuf: %@", self.logTag, error); + return nil; + } + return dataProto; } - (nullable NSData *)buildPlainTextData:(SignalRecipient *)recipient { + NSError *error; + SSKProtoDataMessage *_Nullable dataMessage = [self buildDataMessage:self.recipientId]; + if (!dataMessage) { + OWSFail(@"%@ could not build protobuf: %@", self.logTag, error); + return nil; + } + SSKProtoContentBuilder *contentBuilder = [SSKProtoContentBuilder new]; - contentBuilder.dataMessage = [self buildDataMessage:recipient.recipientId]; - return [[contentBuilder build] data]; + [contentBuilder setDataMessage:dataMessage]; + SSKProtoContent *_Nullable contentProto = [contentBuilder buildAndReturnError:&error]; + if (error || !contentProto) { + OWSFail(@"%@ could not build protobuf: %@", self.logTag, error); + return; + } + NSData *_Nullable contentData = [contentProto serializedDataAndReturnError:&error]; + if (error || !contentData) { + OWSFail(@"%@ could not serialize protobuf: %@", self.logTag, error); + return; + } + return contentData; } - (BOOL)shouldSyncTranscript diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 88ac47fda..ce3516fb6 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -294,32 +294,44 @@ NS_ASSUME_NONNULL_BEGIN return; } - if (envelope.content != nil) { - SSKProtoContent *content = [SSKProtoContent parseFromData:plaintextData]; - DDLogInfo(@"%@ handling content: ", self.logTag, [self descriptionForContent:content]); + if (envelope.hasContent) { + NSError *error; + SSKProtoContent *_Nullable contentProto = [SSKProtoContent parseData:plaintextData error:&error]; + if (error || !contentProto) { + OWSFail(@"%@ could not parse proto: %@", self.logTag, error); + return; + } + DDLogInfo(@"%@ handling content: ", self.logTag, [self descriptionForContent:contentProto]); - if (content.hasSyncMessage) { - [self handleIncomingEnvelope:envelope withSyncMessage:content.syncMessage transaction:transaction]; + if (contentProto.hasSyncMessage) { + [self handleIncomingEnvelope:envelope withSyncMessage:contentProto.syncMessage transaction:transaction]; [[OWSDeviceManager sharedManager] setHasReceivedSyncMessage]; - } else if (content.hasDataMessage) { - [self handleIncomingEnvelope:envelope withDataMessage:content.dataMessage transaction:transaction]; - } else if (content.hasCallMessage) { - [self handleIncomingEnvelope:envelope withCallMessage:content.callMessage]; - } else if (content.hasNullMessage) { + } else if (contentProto.hasDataMessage) { + [self handleIncomingEnvelope:envelope withDataMessage:contentProto.dataMessage transaction:transaction]; + } else if (contentProto.hasCallMessage) { + [self handleIncomingEnvelope:envelope withCallMessage:contentProto.callMessage]; + } else if (contentProto.hasNullMessage) { DDLogInfo(@"%@ Received null message.", self.logTag); - } else if (content.hasReceiptMessage) { - [self handleIncomingEnvelope:envelope withReceiptMessage:content.receiptMessage transaction:transaction]; + } else if (contentProto.hasReceiptMessage) { + [self handleIncomingEnvelope:envelope + withReceiptMessage:contentProto.receiptMessage + transaction:transaction]; } else { DDLogWarn(@"%@ Ignoring envelope. Content with no known payload", self.logTag); } - } else if (envelope.legacyMessage != nil) { // DEPRECATED - Remove after all clients have been upgraded. - SSKProtoDataMessage *dataMessage = - [SSKProtoDataMessage parseFromData:plaintextData]; - DDLogInfo( - @"%@ handling message: ", self.logTag, [self descriptionForDataMessage:dataMessage]); + } else if (envelope.hasLegacyMessage) { // DEPRECATED - Remove after all clients have been upgraded. + NSError *error; + SSKProtoDataMessage *_Nullable dataMessageProto = [SSKProtoDataMessage parseData:plaintextData error:&error]; + if (error || !dataMessageProto) { + OWSFail(@"%@ could not parse proto: %@", self.logTag, error); + return; + } + DDLogInfo(@"%@ handling message: ", + self.logTag, + [self descriptionForDataMessage:dataMessageProto]); - [self handleIncomingEnvelope:envelope withDataMessage:dataMessage transaction:transaction]; + [self handleIncomingEnvelope:envelope withDataMessage:dataMessageProto transaction:transaction]; } else { OWSProdInfoWEnvelope([OWSAnalyticsEvents messageManagerErrorEnvelopeNoActionablePayload], envelope); } @@ -436,12 +448,7 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(receiptMessage); OWSAssert(transaction); - PBArray *messageTimestamps = receiptMessage.timestamp; - NSMutableArray *sentTimestamps = [NSMutableArray new]; - for (int i = 0; i < messageTimestamps.count; i++) { - UInt64 timestamp = [messageTimestamps uint64AtIndex:i]; - [sentTimestamps addObject:@(timestamp)]; - } + NSArray *sentTimestamps = receiptMessage.timestamp; switch (receiptMessage.type) { case SSKProtoReceiptMessageTypeDelivery: diff --git a/SignalServiceKit/src/Messages/OWSProfileKeyMessage.m b/SignalServiceKit/src/Messages/OWSProfileKeyMessage.m index 3df5ca636..291fc3a1b 100644 --- a/SignalServiceKit/src/Messages/OWSProfileKeyMessage.m +++ b/SignalServiceKit/src/Messages/OWSProfileKeyMessage.m @@ -41,7 +41,7 @@ NS_ASSUME_NONNULL_BEGIN return NO; } -- (SSKProtoDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId +- (nullable SSKProtoDataMessage *)buildDataMessage:(NSString *_Nullable)recipientId { OWSAssert(self.thread); @@ -57,7 +57,13 @@ NS_ASSUME_NONNULL_BEGIN [profileManager addUserToProfileWhitelist:recipientId]; } - return [builder build]; + NSError *error; + SSKProtoDataMessage *_Nullable dataProto = [builder buildAndReturnError:&error]; + if (error || !dataProto) { + OWSFail(@"%@ could not build protobuf: %@", self.logTag, error); + return nil; + } + return dataProto; } @end diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index 7b8844c47..a58860a60 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -89,8 +89,11 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_Envelope @objc public let type: SSKProtoEnvelopeType + @objc public let source: String + @objc public let sourceDevice: UInt32 + @objc public let timestamp: UInt64 @objc public var relay: String? { @@ -220,10 +223,29 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_Content @objc public let dataMessage: SSKProtoDataMessage? + @objc public var hasDataMessage: Bool { + return proto.hasDataMessage + } + @objc public let syncMessage: SSKProtoSyncMessage? + @objc public var hasSyncMessage: Bool { + return proto.hasSyncMessage + } + @objc public let callMessage: SSKProtoCallMessage? + @objc public var hasCallMessage: Bool { + return proto.hasCallMessage + } + @objc public let nullMessage: SSKProtoNullMessage? + @objc public var hasNullMessage: Bool { + return proto.hasNullMessage + } + @objc public let receiptMessage: SSKProtoReceiptMessage? + @objc public var hasReceiptMessage: Bool { + return proto.hasReceiptMessage + } private init(proto: SignalServiceProtos_Content, dataMessage: SSKProtoDataMessage?, @@ -701,10 +723,26 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_CallMessage @objc public let offer: SSKProtoCallMessageOffer? + @objc public var hasOffer: Bool { + return proto.hasOffer + } + @objc public let answer: SSKProtoCallMessageAnswer? + @objc public var hasAnswer: Bool { + return proto.hasAnswer + } + @objc public let iceUpdate: [SSKProtoCallMessageIceUpdate] + @objc public let hangup: SSKProtoCallMessageHangup? + @objc public var hasHangup: Bool { + return proto.hasHangup + } + @objc public let busy: SSKProtoCallMessageBusy? + @objc public var hasBusy: Bool { + return proto.hasBusy + } @objc public var profileKey: Data? { guard proto.hasProfileKey else { @@ -836,6 +874,9 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_DataMessage.Quote.QuotedAttachment @objc public let thumbnail: SSKProtoAttachmentPointer? + @objc public var hasThumbnail: Bool { + return proto.hasThumbnail + } @objc public var contentType: String? { guard proto.hasContentType else { @@ -943,7 +984,9 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_DataMessage.Quote @objc public let id: UInt64 + @objc public let author: String + @objc public let attachments: [SSKProtoDataMessageQuoteQuotedAttachment] @objc public var text: String? { @@ -1192,7 +1235,15 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_DataMessage.Contact.Phone - @objc public let value: String + @objc public var value: String? { + guard proto.hasValue else { + return nil + } + return proto.value + } + @objc public var hasValue: Bool { + return proto.hasValue + } @objc public var type: SSKProtoDataMessageContactPhoneType { return SSKProtoDataMessageContactPhone.SSKProtoDataMessageContactPhoneTypeWrap(proto.type) @@ -1211,10 +1262,8 @@ public enum SSKProtoError: Error { return proto.hasLabel } - private init(proto: SignalServiceProtos_DataMessage.Contact.Phone, - value: String) { + private init(proto: SignalServiceProtos_DataMessage.Contact.Phone) { self.proto = proto - self.value = value } @objc @@ -1228,17 +1277,11 @@ public enum SSKProtoError: Error { } fileprivate class func parseProto(_ proto: SignalServiceProtos_DataMessage.Contact.Phone) throws -> SSKProtoDataMessageContactPhone { - guard proto.hasValue else { - throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: value") - } - let value = proto.value - // MARK: - Begin Validation Logic for SSKProtoDataMessageContactPhone - // MARK: - End Validation Logic for SSKProtoDataMessageContactPhone - - let result = SSKProtoDataMessageContactPhone(proto: proto, - value: value) + let result = SSKProtoDataMessageContactPhone(proto: proto) return result } } @@ -1302,7 +1345,15 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_DataMessage.Contact.Email - @objc public let value: String + @objc public var value: String? { + guard proto.hasValue else { + return nil + } + return proto.value + } + @objc public var hasValue: Bool { + return proto.hasValue + } @objc public var type: SSKProtoDataMessageContactEmailType { return SSKProtoDataMessageContactEmail.SSKProtoDataMessageContactEmailTypeWrap(proto.type) @@ -1321,10 +1372,8 @@ public enum SSKProtoError: Error { return proto.hasLabel } - private init(proto: SignalServiceProtos_DataMessage.Contact.Email, - value: String) { + private init(proto: SignalServiceProtos_DataMessage.Contact.Email) { self.proto = proto - self.value = value } @objc @@ -1338,17 +1387,11 @@ public enum SSKProtoError: Error { } fileprivate class func parseProto(_ proto: SignalServiceProtos_DataMessage.Contact.Email) throws -> SSKProtoDataMessageContactEmail { - guard proto.hasValue else { - throw SSKProtoError.invalidProtobuf(description: "\(logTag) missing required field: value") - } - let value = proto.value - // MARK: - Begin Validation Logic for SSKProtoDataMessageContactEmail - // MARK: - End Validation Logic for SSKProtoDataMessageContactEmail - - let result = SSKProtoDataMessageContactEmail(proto: proto, - value: value) + let result = SSKProtoDataMessageContactEmail(proto: proto) return result } } @@ -1573,6 +1616,9 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_DataMessage.Contact.Avatar @objc public let avatar: SSKProtoAttachmentPointer? + @objc public var hasAvatar: Bool { + return proto.hasAvatar + } @objc public var isProfile: Bool { return proto.isProfile @@ -1688,10 +1734,20 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_DataMessage.Contact @objc public let name: SSKProtoDataMessageContactName? + @objc public var hasName: Bool { + return proto.hasName + } + @objc public let number: [SSKProtoDataMessageContactPhone] + @objc public let email: [SSKProtoDataMessageContactEmail] + @objc public let address: [SSKProtoDataMessageContactPostalAddress] + @objc public let avatar: SSKProtoDataMessageContactAvatar? + @objc public var hasAvatar: Bool { + return proto.hasAvatar + } @objc public var organization: String? { guard proto.hasOrganization else { @@ -1871,8 +1927,17 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_DataMessage @objc public let attachments: [SSKProtoAttachmentPointer] + @objc public let group: SSKProtoGroupContext? + @objc public var hasGroup: Bool { + return proto.hasGroup + } + @objc public let quote: SSKProtoDataMessageQuote? + @objc public var hasQuote: Bool { + return proto.hasQuote + } + @objc public let contact: [SSKProtoDataMessageContact] @objc public var body: String? { @@ -2287,6 +2352,9 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_SyncMessage.Sent @objc public let message: SSKProtoDataMessage? + @objc public var hasMessage: Bool { + return proto.hasMessage + } @objc public var destination: String? { guard proto.hasDestination else { @@ -2438,6 +2506,9 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_SyncMessage.Groups @objc public let blob: SSKProtoAttachmentPointer? + @objc public var hasBlob: Bool { + return proto.hasBlob + } private init(proto: SignalServiceProtos_SyncMessage.Groups, blob: SSKProtoAttachmentPointer?) { @@ -2650,6 +2721,7 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_SyncMessage.Read @objc public let sender: String + @objc public let timestamp: UInt64 private init(proto: SignalServiceProtos_SyncMessage.Read, @@ -2814,13 +2886,41 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_SyncMessage @objc public let sent: SSKProtoSyncMessageSent? + @objc public var hasSent: Bool { + return proto.hasSent + } + @objc public let contacts: SSKProtoSyncMessageContacts? + @objc public var hasContacts: Bool { + return proto.hasContacts + } + @objc public let groups: SSKProtoSyncMessageGroups? + @objc public var hasGroups: Bool { + return proto.hasGroups + } + @objc public let request: SSKProtoSyncMessageRequest? + @objc public var hasRequest: Bool { + return proto.hasRequest + } + @objc public let read: [SSKProtoSyncMessageRead] + @objc public let blocked: SSKProtoSyncMessageBlocked? + @objc public var hasBlocked: Bool { + return proto.hasBlocked + } + @objc public let verified: SSKProtoVerified? + @objc public var hasVerified: Bool { + return proto.hasVerified + } + @objc public let configuration: SSKProtoSyncMessageConfiguration? + @objc public var hasConfiguration: Bool { + return proto.hasConfiguration + } @objc public var padding: Data? { guard proto.hasPadding else { @@ -3192,8 +3292,13 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_GroupContext @objc public let id: Data + @objc public let type: SSKProtoGroupContextType + @objc public let avatar: SSKProtoAttachmentPointer? + @objc public var hasAvatar: Bool { + return proto.hasAvatar + } @objc public var name: String? { guard proto.hasName else { @@ -3379,8 +3484,16 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_ContactDetails @objc public let number: String + @objc public let avatar: SSKProtoContactDetailsAvatar? + @objc public var hasAvatar: Bool { + return proto.hasAvatar + } + @objc public let verified: SSKProtoVerified? + @objc public var hasVerified: Bool { + return proto.hasVerified + } @objc public var name: String? { guard proto.hasName else { @@ -3602,7 +3715,11 @@ public enum SSKProtoError: Error { fileprivate let proto: SignalServiceProtos_GroupDetails @objc public let id: Data + @objc public let avatar: SSKProtoGroupDetailsAvatar? + @objc public var hasAvatar: Bool { + return proto.hasAvatar + } @objc public var name: String? { guard proto.hasName else { diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index 86f30894a..e594559a1 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -784,7 +784,6 @@ struct SignalServiceProtos_DataMessage { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - /// @required var value: String { get {return _value ?? String()} set {_value = newValue} @@ -858,7 +857,6 @@ struct SignalServiceProtos_DataMessage { // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. - /// @required var value: String { get {return _value ?? String()} set {_value = newValue}