GithubHelp home page GithubHelp logo

Comments (10)

jrendel avatar jrendel commented on July 30, 2024

Hi James, thats exactly what I had in mind as well in what I've been working on as a "v2" of the library. The first step, as you can see I've done in the develop branch, was to allow instances of KeychainWrapper to be created. So a user could use the default or standard instance to access keychain without having to put any more thought into it. Or a user could create an instance and have the flexibility to configure it.

My next step is exactly as you mentioned, I plan to add an enum that can be passed as an initialization parameter to specify what SecClass you want to access with that instance (generic password, internet password, certificate, etc).

I was also going to do something similar for other options I want to make configurable, such as:

  • device access (must be unlocked, available while app is in background, etc).
  • service name
  • access group name
  • etc

Like I said, I've done the first step of the process. Then I spent some time seeing if I could get subscript access working through generics, but subscript doesn't support generics. Was playing with the idea of a general get/set function that uses generics to determine the type, but I'm not sure I'm happy with that. So I just need to get back to the original plan again.

from swiftkeychainwrapper.

jamesmblair avatar jamesmblair commented on July 30, 2024

Okay, great. Let me know if there's any room for contribution. I'd be happy to help!

What are your thoughts on passing the options as a method parameter instead of to the instance initializer? This way a single instance could be used with items having different values of SecClass. Do you see an issue with designing the API that way?

from swiftkeychainwrapper.

jrendel avatar jrendel commented on July 30, 2024

I'm always open to contribution, so feel free to submit a pull request. But here's where I'm at in my own mind of trying to decide what is best. I've been considering 3 options:

  1. If I look at this with the mindset that immutability is better, than it makes sense to create an instance of the KeychainWrapper, pass in the options you want set for that instance, and then you work with that. Thats how I was starting to approach it. Since there are multiple things that can be set (Service name, access group, accessibility, etc) it gives you a clean interface to set it up and you know exactly what to expect out of that instance of the wrapper and there is no room for misuse.
  2. A more flexible approach would be to create the wrapper, then use setters to change options on it. When you make requests, they use whatever options you have set. But that leaves you with two steps required and I feel thats less clear than the first approach of just initializing the instance with the exact options you want.
  3. The most flexibly option would be passing the options as parameters on every call. Someone actually submitted a pull request with similar changes to this suggestion: cc9af53. With this approach there's even less of a need for an actual instance of the Wrapper. If you just pass all the options as params, you could also just pass an optional service name and access group if you wanted. Then the wrapper doesn't need to maintain any state and goes back to just being a static collection of convenience functions. Which is where it started initially.

Overall, I'm always thinking in terms of what makes the cleanest API and I want to stay true to the original purpose of this library: something simple for a user who has no knowledge of iOS Keychain to pick up and use. That is what has been leading me down the current approach of modeling it after NSUserDefaults: Provide a default instance that clean and the user doesn't have to worry about. Or let a power user create their own instance with the exact options they want. With that in mind, I'm still leaning towards my option 1 design.

from swiftkeychainwrapper.

jamesmblair avatar jamesmblair commented on July 30, 2024

I suppose the approach I was considering is a hybrid of your first and third options.

It seems the purpose of this class is to provide an object-oriented wrapper of the unwieldy, procedural C-style APIs of the security framework. My thinking is that the attributes a Keychain class should encapsulate are options like the service name and access group, while options like accessibility and SecClass are really attributes of individual keychain items, not the keychain itself. The keychain item, with all of it's options, could even be abstracted into it's own KeychainItem data structure if desired.

To that end, I would propose using Option 1 for keychain level options, but Option 3 for options at the item level. I do like your approach of modeling after NSUserDefaults with a default instance initialized with the most common options. It keeps things very familiar and Cocoa-like. What are your thoughts on separating the options?

from swiftkeychainwrapper.

jrendel avatar jrendel commented on July 30, 2024

Thinking through things again, I can agree with that. Your pull request looks great and I've merged it in.

I made a couple tweaks:

  1. Require the "withOptions" tag when calling setupKeychainQueryDictionaryForKey. I know its an private function only used internally to the class and this is unnecessary, but I still prefer the explicit param name for clarity.
  2. Updated setupKeychainQueryDictionaryForKey(keyName:) to generate a default option set and then just call setupKeychainQueryDictionaryForKey(keyName:withOptions:). This way the base implementation is only done once.

from swiftkeychainwrapper.

jrendel avatar jrendel commented on July 30, 2024

While we're bouncing ideas around, what are your thoughts on using two functions as you did:

    public func integerForKey(keyName: String) -> Int? {
        guard let numberValue = self.objectForKey(keyName) as? NSNumber else {
            return nil
        }

        return numberValue.integerValue
    }

    public func integerForKey(keyName: String, withOptions options: KeychainItemOptions) -> Int? {
        guard let numberValue = self.objectForKey(keyName, withOptions: options) as? NSNumber else {
            return nil
        }

        return numberValue.integerValue
    }

Vs. combining them into a single function with an optional parameter for the options (and give it a default of nil)? I have mixed feelings on this and I actually like the 2 separate function calls as it makes for a distinct API. At the same time, Swift is encouraging the use of default function parameters to cut down on needing to duplicate function with only on parameter changed.

If you look at the changes I made to setupKeychainQueryDictionaryForKey, those could easily be combine into a single function with a signature like:

setupKeychainQueryDictionaryForKey(keyName: String, withOptions options: KeychainItemOptions? = nil)

And then the first step could just be to check if the options are nil, and if so create the default ones. Once thats in place, every other pair of functions could be merged into a single function and just pass along the options it was given. Those functions wouldn't need to care if the options were nil or not.

Thoughts?

from swiftkeychainwrapper.

jrendel avatar jrendel commented on July 30, 2024

Hey James, question for you if you're still around: I was trying to put together some simple test cases to test the different SecClass options and realized there's more to them than just chaining the SecClass. For example, to use InternetPassword, it looks like you need to include additional information beyond what is supported by KeychainWrapper right now.

I'm thinking I may revert some of the changes done for now to only support changing the Accessibility option and only allow the wrapper to support Generic Passwords for now. However, since you initiated this pull request, I thought I would check if you were currently using the functionality to change the SecClass to something other than Generic Password.

from swiftkeychainwrapper.

jamesmblair avatar jamesmblair commented on July 30, 2024

Hey. Sorry, I didn't mean to totally ghost you on this. I've had a really busy summer with work and this completely fell off my radar. By all means, please revert if I've broken something badly. :)

That makes a lot of sense now that you mention it. My first thought is that maybe using associated values with the enum might be a good approach. In other words, if the enum value were GenericPassword, it would have an associated value (probably a struct) encapsulating the necessary data/options for a generic password. What are your thoughts on that approach?

In any case, I'm sorry if I submitted something broken. And I'd love to continue discussion on this topic, including how to properly test the implementation.

from swiftkeychainwrapper.

jrendel avatar jrendel commented on July 30, 2024

No worries, its not a big deal. I know how it gets and I've been slowly pecking away at this. But I'd like to wrap up the changes for now, so I will probably go forward with only supporting Generic Password and just allow changing Accessibility type.

Using an enum with associated values may work well, I would have to spend some more time digging into what is actually required for the different SecClass types and think about what makes sense. But I have mixed feelings on adding further support beyond GenericPassword. Mostly due to the time I can put into supporting this. And there are also other Keychain libraries out there that do allow more flexibility. My goal with this initially was to simplify basic use of the iOS Keychain, for people that just want to be able to use it without understanding all the more complex use cases, so for now I may try and limit the scope of what I add.

from swiftkeychainwrapper.

jrendel avatar jrendel commented on July 30, 2024

For now I've decided am happy to only support kSecClassGenericPassword as I don't have the time to implement, test and maintain other implementations. And there are other Keychain libraries out there that provide addition SecCall support if needed.

from swiftkeychainwrapper.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.