diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 113f0c3..e5de221 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -8,7 +8,7 @@ PODS: - FBSnapshotTestCase/SwiftSupport (2.1.4): - FBSnapshotTestCase/Core - HandyJSON (5.0.0-beta.1) - - SmartCodable (4.2.1) + - SmartCodable (4.2.2) - SnapKit (5.6.0) DEPENDENCIES: @@ -39,7 +39,7 @@ SPEC CHECKSUMS: CleanJSON: 910a36465ce4829e264a902ccf6d1455fdd9f980 FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a HandyJSON: 582477127ab3ab65bd2e471815f1a7b846856978 - SmartCodable: ad311e84a90b09a85ba8f963ec16d30bc11fed19 + SmartCodable: 4f52d801dbd15856fabb69110d24f31f49145734 SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25 PODFILE CHECKSUM: 7f3af03f81934df0c035518074a7abbec8fa9d3f diff --git a/Example/Pods/Local Podspecs/SmartCodable.podspec.json b/Example/Pods/Local Podspecs/SmartCodable.podspec.json index 360a8f7..79211bd 100644 --- a/Example/Pods/Local Podspecs/SmartCodable.podspec.json +++ b/Example/Pods/Local Podspecs/SmartCodable.podspec.json @@ -1,6 +1,6 @@ { "name": "SmartCodable", - "version": "4.2.1", + "version": "4.2.2", "summary": "数据解析库", "homepage": "https://github.com/intsig171", "license": { @@ -12,7 +12,7 @@ }, "source": { "git": "https://github.com/intsig171/SmartCodable.git", - "tag": "4.2.1" + "tag": "4.2.2" }, "platforms": { "ios": "11.0", diff --git a/Example/Pods/Manifest.lock b/Example/Pods/Manifest.lock index 113f0c3..e5de221 100644 --- a/Example/Pods/Manifest.lock +++ b/Example/Pods/Manifest.lock @@ -8,7 +8,7 @@ PODS: - FBSnapshotTestCase/SwiftSupport (2.1.4): - FBSnapshotTestCase/Core - HandyJSON (5.0.0-beta.1) - - SmartCodable (4.2.1) + - SmartCodable (4.2.2) - SnapKit (5.6.0) DEPENDENCIES: @@ -39,7 +39,7 @@ SPEC CHECKSUMS: CleanJSON: 910a36465ce4829e264a902ccf6d1455fdd9f980 FBSnapshotTestCase: 094f9f314decbabe373b87cc339bea235a63e07a HandyJSON: 582477127ab3ab65bd2e471815f1a7b846856978 - SmartCodable: ad311e84a90b09a85ba8f963ec16d30bc11fed19 + SmartCodable: 4f52d801dbd15856fabb69110d24f31f49145734 SnapKit: e01d52ebb8ddbc333eefe2132acf85c8227d9c25 PODFILE CHECKSUM: 7f3af03f81934df0c035518074a7abbec8fa9d3f diff --git a/Example/Pods/Target Support Files/SmartCodable/SmartCodable-Info.plist b/Example/Pods/Target Support Files/SmartCodable/SmartCodable-Info.plist index 03d8a17..370c557 100644 --- a/Example/Pods/Target Support Files/SmartCodable/SmartCodable-Info.plist +++ b/Example/Pods/Target Support Files/SmartCodable/SmartCodable-Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 4.2.1 + 4.2.2 CFBundleSignature ???? CFBundleVersion diff --git a/Example/SmartCodable.xcodeproj/project.pbxproj b/Example/SmartCodable.xcodeproj/project.pbxproj index c6009fc..c89ad5b 100644 --- a/Example/SmartCodable.xcodeproj/project.pbxproj +++ b/Example/SmartCodable.xcodeproj/project.pbxproj @@ -823,10 +823,12 @@ TargetAttributes = { 607FACCF1AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; + DevelopmentTeam = RA2G5HURUT; LastSwiftMigration = 0900; }; 607FACE41AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; + DevelopmentTeam = RA2G5HURUT; LastSwiftMigration = 0900; TestTargetID = 607FACCF1AFB9204008FA782; }; @@ -1239,6 +1241,7 @@ baseConfigurationReference = BC656B63C9031B22DEB5C8EB /* Pods-SmartCodable_Example.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = RA2G5HURUT; INFOPLIST_FILE = SmartCodable/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1255,6 +1258,7 @@ baseConfigurationReference = AD8215B5E8E50F277C1C4356 /* Pods-SmartCodable_Example.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = RA2G5HURUT; INFOPLIST_FILE = SmartCodable/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1270,6 +1274,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = DB7246035010143E03C7616B /* Pods-SmartCodable_Tests.debug.xcconfig */; buildSettings = { + DEVELOPMENT_TEAM = RA2G5HURUT; FRAMEWORK_SEARCH_PATHS = ( "$(PLATFORM_DIR)/Developer/Library/Frameworks", "$(inherited)", @@ -1293,6 +1298,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = AFEADC35BC2373F1DC69B73B /* Pods-SmartCodable_Tests.release.xcconfig */; buildSettings = { + DEVELOPMENT_TEAM = RA2G5HURUT; FRAMEWORK_SEARCH_PATHS = ( "$(PLATFORM_DIR)/Developer/Library/Frameworks", "$(inherited)", diff --git "a/Example/SmartCodable/Smart/1.Introduce(\344\275\277\347\224\250\344\273\213\347\273\215)/Introduce_12ViewController.swift" "b/Example/SmartCodable/Smart/1.Introduce(\344\275\277\347\224\250\344\273\213\347\273\215)/Introduce_12ViewController.swift" index b87d467..3565d0b 100644 --- "a/Example/SmartCodable/Smart/1.Introduce(\344\275\277\347\224\250\344\273\213\347\273\215)/Introduce_12ViewController.swift" +++ "b/Example/SmartCodable/Smart/1.Introduce(\344\275\277\347\224\250\344\273\213\347\273\215)/Introduce_12ViewController.swift" @@ -13,15 +13,19 @@ import SmartCodable class Introduce_12ViewController: BaseViewController { var cancellables = Set() + var model: PublishedModel? + override func viewDidLoad() { super.viewDidLoad() let dict: [String: Any] = [ - "newName": 1, + "name": 1, "age": "333333" ] if let model = PublishedModel.deserialize(from: dict) { + + self.model = model print("反序列化后的 name 值: \(model.name)") // 正确访问 name 属性的 Publisher @@ -31,10 +35,14 @@ class Introduce_12ViewController: BaseViewController { } .store(in: &cancellables) - // 修改 model 的 name 属性 - model.name = "Updated iOS Developer" + } } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + // 修改 model 的 name 属性 + model?.name = "Updated iOS Developer" + } } // 定义 PublishedModel,并实现反序列化 @@ -43,28 +51,22 @@ class PublishedModel: ObservableObject, SmartCodable { @SmartPublished var name: String = "iOS Developer" - - static func mappingForKey() -> [SmartKeyTransformer]? { - [CodingKeys.name <--- "newName"] - } - -// static func mappingForValue() -> [SmartValueTransformer]? { -// [ -// CodingKeys.name <--- PublishedValueTransformer(), -// ] +// +// static func mappingForKey() -> [SmartKeyTransformer]? { +// [CodingKeys.name <--- "newName"] // } } -struct PublishedValueTransformer: ValueTransformable { - func transformFromJSON(_ value: Any) -> String? { - return "good" - } - - func transformToJSON(_ value: String) -> String? { - return "gooooooood" - } - - typealias Object = String - - typealias JSON = String -} +//struct PublishedValueTransformer: ValueTransformable { +// func transformFromJSON(_ value: Any) -> String? { +// return "good" +// } +// +// func transformToJSON(_ value: String) -> String? { +// return "gooooooood" +// } +// +// typealias Object = String +// +// typealias JSON = String +//} diff --git a/Example/SmartCodable/Test2ViewController.swift b/Example/SmartCodable/Test2ViewController.swift index 6324efa..e8fcac0 100644 --- a/Example/SmartCodable/Test2ViewController.swift +++ b/Example/SmartCodable/Test2ViewController.swift @@ -16,22 +16,17 @@ class Test2ViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() - let model = Model() - - let json = model.toDictionary() - - print(json) + let dict = [ + "name": 1.22222 + ] + let model = Model.deserialize(from: dict) + print(model) } struct Model: SmartCodable { - var name: String = "Mccc" - - @IgnoredKey(supportEncode: false) - var ignore1: String = "忽略的key1" + var name: String = "" - @IgnoredKey(supportEncode: true) - var ignore2: String = "忽略的key2" } } diff --git a/Example/SmartCodable/TestViewController.swift b/Example/SmartCodable/TestViewController.swift index d45658f..0d67a2a 100644 --- a/Example/SmartCodable/TestViewController.swift +++ b/Example/SmartCodable/TestViewController.swift @@ -31,13 +31,10 @@ import BTPrint */ - class TestViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() - - } } diff --git a/SmartCodable.podspec b/SmartCodable.podspec index 1027ed9..8ce0dd1 100644 --- a/SmartCodable.podspec +++ b/SmartCodable.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |s| s.name = 'SmartCodable' - s.version = '4.2.1' + s.version = '4.2.2' s.summary = '数据解析库' s.homepage = 'https://github.com/intsig171' diff --git a/SmartCodable/Classes/SmartType/SmartPublished.swift b/SmartCodable/Classes/SmartType/SmartPublished.swift index 1b0e30a..22a435c 100644 --- a/SmartCodable/Classes/SmartType/SmartPublished.swift +++ b/SmartCodable/Classes/SmartType/SmartPublished.swift @@ -26,50 +26,80 @@ public protocol SmartPublishedProtocol { static func createInstance(with value: Any) -> Self? } -/// PublishedContainer -/// @Published使属性成为一个发布者,自动发布变更。 -/// ObservableObject可以被SwiftUI或其他观察者订阅以监听其变化。 -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) -public class PublishedContainer: ObservableObject { - @Published public var wrappedValue: Value - - public init(wrappedValue: Value) { - self.wrappedValue = wrappedValue - } -} - -/// 属性包装器SmartPublished,允许在声明属性时附加额外的行为。 -/// container使用PublishedContainer管理值和发布功能。 -/// wrappedValue提供对实际值的访问。 /// projectedValue提供一个发布者,可供订阅。 @propertyWrapper @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) public struct SmartPublished: Codable { - private var container: PublishedContainer + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let value = try container.decode(Value.self) + self.wrappedValue = value + publisher = Publisher(wrappedValue) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.wrappedValue) + } public var wrappedValue: Value { - get { container.wrappedValue } - set { container.wrappedValue = newValue } + // willSet 观察器在 wrappedValue 被修改前调用,会将新的值通过 publisher 发送出去,从而通知所有的订阅者。这实现了数据更新的响应式特性。 + willSet { + publisher.subject.send(newValue) + } } - public var projectedValue: Published.Publisher { - container.$wrappedValue + public var projectedValue: Publisher { + publisher } - public init(wrappedValue: Value) { - self.container = PublishedContainer(wrappedValue: wrappedValue) + private var publisher: Publisher + + public struct Publisher: Combine.Publisher { + public typealias Output = Value + public typealias Failure = Never + + // CurrentValueSubject 是 Combine 中的一种 Subject,它会保存当前值并向新订阅者发送当前值。相比于 PassthroughSubject,它在初始化时就要求有一个初始值,因此更适合这种包装属性的场景。 + var subject: CurrentValueSubject + + // 这个方法实现了 Publisher 协议,将 subscriber 传递给 subject,从而将订阅者连接到这个发布者上。 + public func receive(subscriber: S) where S: Subscriber, Self.Failure == S.Failure, Self.Output == S.Input { + subject.subscribe(subscriber) + } + + // Publisher 的构造函数接受一个初始值,并将其传递给 CurrentValueSubject 的初始化方法。 + init(_ output: Output) { + subject = .init(output) + } } - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let value = try container.decode(Value.self) - self.container = PublishedContainer(wrappedValue: value) + public init(wrappedValue: Value) { + self.wrappedValue = wrappedValue + publisher = Publisher(wrappedValue) } - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.wrappedValue) + + /// 这个下标实现了对属性包装器的自定义访问逻辑,用于在包装器内自定义 wrappedValue 的访问和修改行为。 + /// 参数解析: + /// observed:观察者,即外部的 ObservableObject 实例。 + /// wrappedKeyPath:指向被包装值的引用键路径。 + /// storageKeyPath:指向属性包装器自身的引用键路径。 + public static subscript( + _enclosingInstance observed: OuterSelf, + wrapped wrappedKeyPath: ReferenceWritableKeyPath, + storage storageKeyPath: ReferenceWritableKeyPath + ) -> Value { + get { + observed[keyPath: storageKeyPath].wrappedValue + } + set { + // 在设置新值之前,如果 observed 的 objectWillChange 属性是 ObservableObjectPublisher 类型,则它会发送通知,确保在属性值更新之前,订阅者能收到通知。 + if let subject = observed.objectWillChange as? ObservableObjectPublisher { + subject.send() // 修改 wrappedValue 之前 + observed[keyPath: storageKeyPath].wrappedValue = newValue + } + } } }