Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] rewrite EZConfiguration using Swift #335

Merged
merged 21 commits into from
Jan 23, 2024

Conversation

NeverAgain11
Copy link
Collaborator

@CanglongCl
Copy link
Collaborator

@Defaults.Default property wrapper should be only used in SwiftUI since it introduce a SwiftUI.ObservedObject for UI tracking which we don't need here.

I wrote a property wrapper for you. Have a try.

@propertyWrapper
struct DefaultsWrapper<T: Defaults.Serializable> {
    var wrappedValue: T {
        get {
            Defaults[key]
        } set {
            Defaults[key] = newValue
        }
    }

    /// Initialize a property binding to Defaults value.
    /// - Parameters:
    ///   - key: key in Defaults
    ///   - onUpdated: the function called on value changes
    ///   - callInitial: the function called on initialize the property
    init(_ key: Defaults.Key<T>, onUpdated: @escaping (_ oldValue: T, _ newValue: T) -> Void, onInitial: ((_ value: T) -> Void)?) {
        self.key = key
        if let onInitial {
            onInitial(Defaults[key])
        }
        observer = Defaults.observe(key) { change in
            onUpdated(change.oldValue, change.newValue)
        }
    }

    private let observer: Defaults.Observation

    let key: Defaults.Key<T>
}

@DefaultsWrapper(
    .firstLanguage,
    onUpdated: { _, newValue in
        logSettings(["first_language": newValue])
    },
    onInitial: { logSettings(["first_language": $0]) }
)
var firstLanguage

@CanglongCl
Copy link
Collaborator

CanglongCl commented Jan 17, 2024

I implement intelligentQueryTextType for your reference.

extension Defaults.Keys {
    static func intelligentQueryTextType(for serviceType: ServiceType) -> Key<EZQueryTextType> {
        let key = EZConstKey.constkey("IntelligentQueryTextType", serviceType: serviceType)
        return .init(key, default: EZQueryTextType(rawValue: 7))
    }
}

extension EZQueryTextType: Defaults.Serializable {
    public static var bridge: Bridge = Bridge()

    public struct Bridge: Defaults.Bridge {
        public func serialize(_ value: EZQueryTextType?) -> String? {
            guard let value else { return "7" }
            return "\(value.rawValue)"
        }
        
        public func deserialize(_ object: String?) -> EZQueryTextType? {
            guard let object else { return nil }
            return EZQueryTextType(rawValue: UInt(object) ?? 7)
        }
        
        public typealias Value = EZQueryTextType

        public typealias Serializable = String
    }
}

Use with

Defaults[.intelligentQueryTextType(for: .ali)]

Not tested. Please check if it works. I think it should work.)

@NeverAgain11
Copy link
Collaborator Author

@CanglongCl
DefaultsWrapper has an error.
image

@CanglongCl
Copy link
Collaborator

CanglongCl commented Jan 17, 2024

@CanglongCl DefaultsWrapper has an error. image

Try make func logSettings(_ parameters: [String: Any]) to static func logSettings(_ parameters: [String: Any]) and make func didSetFirstLanguage() to static func didSetFirstLanguage(newValue: Language) since they all do not depend on Configuration instance.

@NeverAgain11
Copy link
Collaborator Author

Defaults.observe already meet the need for observation.

@tisfeng
Copy link
Owner

tisfeng commented Jan 17, 2024

@NeverAgain11 Please resolve conflicts.

AkaShark
AkaShark previously approved these changes Jan 17, 2024
@NeverAgain11
Copy link
Collaborator Author

NeverAgain11 commented Jan 17, 2024

@NeverAgain11 Please resolve conflicts.

@tisfeng Done.

@tisfeng
Copy link
Owner

tisfeng commented Jan 17, 2024

@NeverAgain11 This code looks fine, but we need to test their functionality in real use.

After rewriting EZConfiguration, you should try to completely replace the previous objc EZConfiguration with the new Swift Configuration and make sure that both the new SwiftUI settings page and the previous settings page work properly.

In addition, I noticed that both NewConfiguration.swift and Configuration.swift exist, maybe we need to merge them and move the Swift code all under a new group, moving to the Configuration.swift file.

@NeverAgain11
Copy link
Collaborator Author

@tisfeng The property in the swift configuration are using Struct. this is not accessible by the objc class.

@tisfeng
Copy link
Owner

tisfeng commented Jan 17, 2024

I'm not sure exactly what you are referring to, can you elaborate using code? Is it possible to use Class instead of Struct? Whatever the problem is, we need to figure out how to fix it.

If some of the Swift properties really don't work in objc, we can start by replacing EZConfiguration in Swift/SwiftUI with Configuration.swift and make sure that the new settings page functionality works fine. EZConfiguration in objc can be left unchanged for now.

@tisfeng
Copy link
Owner

tisfeng commented Jan 17, 2024

I tried that, and it's probably not really a good idea to completely replace objc EZConfiguration with Configuration.swift, so let's just go ahead and replace the one in the Swift code.

@CanglongCl
Copy link
Collaborator

I'm not sure exactly what you are referring to, can you elaborate using code? Is it possible to use Class instead of Struct? Whatever the problem is, we need to figure out how to fix it.

If some of the Swift properties really don't work in objc, we can start by replacing EZConfiguration in Swift/SwiftUI with Configuration.swift and make sure that the new settings page functionality works fine. EZConfiguration in objc can be left unchanged for now.

I don't think we really need a Configuration in Swift since we can access the defaults via Defaults[.defaultKey]. Maybe we can just leave NewConfiguration.swift to be the only observer for the user defaults globally (which means just delete codes like logSetting in setSomething in EZConfiguration.m).

@CanglongCl
Copy link
Collaborator

CanglongCl commented Jan 21, 2024

@tisfeng I've resolved the issue. It seems like an issue cause by UserDefaults instead of Defaults package. I found some information here on Stack OverFlow.

Now I replaced the old code with

Defaults.publisher(.hideMenuBarIcon)
                .removeDuplicates()
                .sink { [weak self] _ in
                    self?.didSetHideMenuBarIcon()
                }

where .removeDuplicates() should handle the problem. However, now only the changes of the value will trigger it instead of each set operation. I think it is not a problem here.

cc @NeverAgain11 Please review the changes.

CanglongCl
CanglongCl previously approved these changes Jan 21, 2024
@NeverAgain11
Copy link
Collaborator Author

@tisfeng I've resolved the issue. It seems like an issue cause by UserDefaults instead of Defaults package. I found some information here on Stack OverFlow.

Now I replaced the old code with

Defaults.publisher(.hideMenuBarIcon)
                .removeDuplicates()
                .sink { [weak self] _ in
                    self?.didSetHideMenuBarIcon()
                }

where .removeDuplicates() should handle the problem. However, now only the changes of the value will trigger it instead of each set operation. I think it is not a problem here.

cc @NeverAgain11 Please review the changes.

It's works fine.
The DefaultsWrapper easily causes the Simultaneous Accesses error, so I change the type from struct to class.

@tisfeng
Copy link
Owner

tisfeng commented Jan 22, 2024

Can we remove #import "EZConfiguration.h" from Easydict-Bridging-Header.h now ?

#import "EZConfiguration.h"

@tisfeng
Copy link
Owner

tisfeng commented Jan 22, 2024

In addition, I noticed that both NewConfiguration.swift and Configuration.swift exist, maybe we need to merge them and move the Swift code all under a new group, moving to the Configuration.swift file.

Again, can we merge NewConfiguration.swift and Configuration.swift? They have similar names and functions, which can be confusing.

@tisfeng tisfeng merged commit 18ae512 into tisfeng:dev Jan 23, 2024
5 checks passed
@tisfeng
Copy link
Owner

tisfeng commented Jan 23, 2024

Thank you all, the new settings page is mostly working now, with probably a few minor issues, hopefully we can finalize and polish it up soon and get it out to users.

if windowType == .mini {
return true
}
return UserDefaults.standard.string(forKey: key) ?? defaultValue == "1"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

上面代码有问题,会导致 mini 窗口一直开启智能查询模式,且无法手动关闭 #502 (comment)

以下是之前功能的 objc 代码。

- (BOOL)intelligentQueryModeForWindowType:(EZWindowType)windowType {
    NSString *key = [EZConstKey constkey:EZIntelligentQueryModeKey windowType:windowType];
    
    NSString *defaultValue = @"0";
    // Turn on intelligent query mode by default in mini window.
    if (windowType == EZWindowTypeMini) {
        defaultValue = @"1";
    }
    
    NSString *stringValue = [NSUserDefaults mm_readString:key defaultValue:defaultValue];
    return [stringValue boolValue];
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants