Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

New Settings & Typed Preferences #61

Merged
merged 23 commits into from
Jul 11, 2018
Merged

New Settings & Typed Preferences #61

merged 23 commits into from
Jul 11, 2018

Conversation

kylehickinson
Copy link
Collaborator

@kylehickinson kylehickinson commented Jun 22, 2018

An attempt at cleaning up brave preferences from being scattered throughout to a single spot with static types which can be observed where their changes are relevant rather than the Settings screen reaching for the AppDelegate/BrowserViewController/etc. and altering app state.

Edit: Done/Deferred

Migration

  • Moving from PrefsPreferences.Option<T> using new keys and standard user defaults over the suite FF used previously.
  • Moving users previous PIN info to shared app container instead of default keychain (or simply reverting back to default keychain over current FF implementation)
  • Moving non-settings-screen based preferences used (such as DDG popup) into Preferences

For settings that in browser-ios used BoolSetting without the change affecting something else in the app directly in BraveSettingsView, this implementation does the same.

General

Privacy

Security

  • Passcode (Used FF's implementation over the old implementation)

Shields

  • Block Ads & Tracking
  • HTTPS Everywhere
  • Block Phishing & Malware
  • Block Scripts
  • Fingerprinting Protection: See BrowserViewController.swift:L2714
  • Regional Ad-blocking† (Waiting for AdBlocker/Adblock Rules #71)

Support

  • Send crash reports and metrics
  • Report a bug
  • Privacy Policy
  • Terms of Use

- Setting is included however implementation is waiting for other features to be ported

fileprivate init(key: String, default: ValueType, container: UserDefaults = UserDefaults.standard) {
self.key = key
self.container = container
value = (container.value(forKey: key) as? ValueType) ?? `default`
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure about that fallback to default value.
There are times where we want to see if a preference wasn't set yet.

Example: after you enter private mode there is a popup about switching search engine to duckduckgo.
Our logic goes like this:

  • no pref set - we show the popup
  • pref set to false - we don't show the popup but a small info label about ddg private search
  • pref set to true - don't show popup and callout label

As a workaround we could create an Int enum with not set case

This of course assumes we are going to use Preferences outside of settings. It's good for settings now as each setting option must have a default value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@iccub My initial thought on that use-case you described was that it's perfect for a Bool? value type which gives you nil, false, and true as options, where the default would be nil (not set).

Since (NS)UserDefualts removes the key-value from the dictionary when you set nil it's basically "unsetting" it if were to ever reset it back to nil.

And since you already were doing checks to see if the pref was set you already were doing some sort of unwrapping.

As for other options: I tried an option where Option.value was an enum Value<ValueType> where it could be .set(T) or .notSet but it made things a bit more complicated than needed around getting/setting the value.

Another option is just to add a computed property to Option like so:

var isSet: Bool {
  return container.value(forKey: key) != nil
}

However then we'd be dealing with a more confusing check when using the value itself (i.e. The bool default would be false, but isSet might return true).

Let me know if this all makes sense. I think we can do most use-cases including ones where you check if its initially set by simply using Optionals and it makes it a bit more clear to use.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That makes sense thanks for the clarification. Optionals with default nils should be enough.

@tmancey
Copy link
Collaborator

tmancey commented Jun 25, 2018

@kylehickinson Great work. Just a few things, such as breaking down viewDidLoad and hard coded values such as "static let TableViewHeaderFooterHeight = CGFloat(44)" but as you say this is work in progress.

@kylehickinson
Copy link
Collaborator Author

@tmancey Hah yeah I just copy pasted that over from brave/browser-ios quickly since I was missing some colours. I don't even think TableViewHeaderFooterHeight is used, but I'll remove all those before finalizing the PR

@kylehickinson kylehickinson changed the title [WIP] New Settings & Typed Preferences New Settings & Typed Preferences Jul 4, 2018
@kylehickinson
Copy link
Collaborator Author

This has reached a good point to move on now. There is one outstanding thing that isn't in the task list: User Referral Program (URP #63) has a few prefs saved which aren't migrated or recreated in Preferences at the moment.

When URP is added it should probably move its preferences to a Preferences extension and migrate old values from the old Prefs object.

@kylehickinson
Copy link
Collaborator Author

Also another thing missing is test cases to test migration & storing preferences via Preferences

Copy link
Collaborator

@iccub iccub left a comment

Choose a reason for hiding this comment

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

Looks very nice to me. Found mostly linter stuff and one place where old preferences are used.

I know some of this code was ported from v1 and you didn't change much to it.


/// Setup default URLCache settings for Brave
public func setupBraveDefaults() {
memoryCapacity = 6 * 1024 * 1024; // 6 MB
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any reason for semicolons here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy and paste :)

// Migration.swift
// Client
//
// Created by Kyle Hickinson on 2018-07-03.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use MPL license.
On a side note, is there a way to set a template to replace default comments on top of a file? I know Android Studio has it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Missed this one whoops, and hmm quick search: https://stackoverflow.com/a/46345626

Maybe I'll try adding this to this PR

container.set(value, forKey: key)
container.synchronize()

if observers.count() > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why check for count > 0 if we are doing forEach anyway?


var isCancellable: Bool = true {
didSet {
if isCancellable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can be inlined with that ? operator

}
}

private lazy var cancelBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(self.dismissAnimated))
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can remove self from selector's method.


let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let clearAction = UIAlertAction(title: Strings.ClearPrivateData, style: .destructive) { (_) in
self.profile.prefs.setObject(self.toggles, forKey: TogglesPrefKey)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't this use the new preferences too?

let dir = library.appendingPathComponent(folder)
let contents = try manager.contentsOfDirectory(atPath: dir.path)
var contents = try manager.contentsOfDirectory(atPath: dir.path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

It can stay as let.

// OptionSelectionViewController.swift
// Client
//
// Created by Kyle Hickinson on 2018-06-25.
Copy link
Collaborator

Choose a reason for hiding this comment

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

MPL license.

if let value = userDefaults?.object(forKey: profileKey) as? T {
option.value = value
userDefaults?.removeObject(forKey: profileKey)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We could add a log here if value was not unwrapped, to catch any typos with preference keys from v1.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Only worry I have is being spammed for keys which aren't set at all yet (i.e. popupForDDG if the user never used private tabs yet will not exist)

static let defaultContainer = UserDefaults(suiteName: AppInfo.sharedContainerIdentifier)!
}

extension Preferences {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I see two Preferences extensions, I guess one refers to settings only and second is for other things, maybe put a MARK: comment to make it more visible?

override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = SettingsUX.TableViewHeaderBackgroundColor
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(self.dismissAnimated))
navigationItem.rightBarButtonItem = isCancellable ? cancelBarButtonItem : nil
Copy link
Contributor

Choose a reason for hiding this comment

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

Despite the minimal duplication, this probably need to be abstracted. Rather than giving isCancellable a default value, could set it in the init (although inits with VCs are annoying). Or use a separate function that they both call.

import Shared
import JavaScriptCore

class BraveWebView: TabWebView {
Copy link
Contributor

Choose a reason for hiding this comment

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

Depends what type of mechanism is in v2 currently. Browser-ios limits number of in-memory tabs, and will start to deallocate them if it reaches a certain threshold. v2 may already have similar "caching" logic in place to properly deallocate webviews while leaving the underlying data intact.

// TODO: add API to avoid add/remove
self.tabManager.removeTab(self.tabManager.addTab())
}
// }
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this hanging paren be removed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Its actually part of the // TODO above. When we have Private Browsing back in we need to re-visit this and re-enable the logic commented out around this.

Preferences.Privacy.clearPrivateDataToggles.value = self.toggles
self.clearButtonEnabled = false

// NotificationCenter.default.removeObserver(self)
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a notification reset, if not needed clear it out.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm going to remove this for now anyways, but we may end up re-visiting if we use BraveWebView to bring back counting and have to some sort of notification system for letting the controller know when that count updates

@kylehickinson
Copy link
Collaborator Author

@jhreis Let me know if you still want BraveWebView removed for the time being

@jhreis jhreis mentioned this pull request Jul 11, 2018
5 tasks
Copy link
Contributor

@jhreis jhreis left a comment

Choose a reason for hiding this comment

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

There are a couple small things (e.g. lazy vars), but given that most of this code is just moved from current project, I really would prefer to get it in. Related items are so minor and not technically incorrect, so no point in blocking on them rn. Nice work. Huge PR.

<plist version="1.0">
<dict>
<key>FILEHEADER</key>
<string> This Source Code Form is subject to the terms of the Mozilla Public
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this is needed, it is just painfully dumb... haha.

@jhreis jhreis merged commit c2236f0 into development Jul 11, 2018
@jhreis jhreis deleted the preferences branch July 11, 2018 15:22
@kylehickinson kylehickinson mentioned this pull request Jul 24, 2018
@jhreis jhreis mentioned this pull request Aug 24, 2018
5 tasks
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants