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

watermark feature for iOS App #1420

Closed
wants to merge 12 commits into from
Closed

Conversation

blicknix
Copy link

@blicknix blicknix commented Nov 6, 2024

Description

We implemented a watermark feature which can be enabled with MDM Tools.
Also we implemented a feature which allows to add a notification if a screenshot was made in the app.

Related Issue

Motivation and Context

For audit reasons we want to add watermarks to each screen which shows personal data.

How Has This Been Tested?

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • Added an issue with details about all relevant changes in the iOS documentation repository.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • Added changelog files for the fixed issues in folder changelog/unreleased

@CLAassistant
Copy link

CLAassistant commented Nov 6, 2024

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ DeepDiver1975
❌ b1schumacher
You have signed the CLA already but the status is still pending? Let us recheck it.

@DeepDiver1975
Copy link
Member

@blicknix THX for your contribution - please sign the CLA as pointed out by the bot above. THX

"ar" : {
"stringUnit" : {
"state" : "translated",
"value" : "أنا أفهم"
Copy link
Member

Choose a reason for hiding this comment

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

For the future: translations will happen on https://app.transifex.com/owncloud-org/owncloud-ios/dashboard/

Any change done in the source code will be overwritten on a daily basis once merged.

Copy link
Contributor

@felix-schwarz felix-schwarz left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution.

Please see my comments regarding specific code snippets.

On a more general note, I'd like to add that modularity is an important aspect in this project and additions like yours should be as modular as possible. Right now, your changes are woven in directly in many places that don't have any direct contextual relationship to the added features.

To fit in the project, I'd therefore like to ask you to change that structure:

  • please create a new folder ownCloudAppShared/User Interface/Watermark and put your code additions in that folder as far as possible
  • please put your code in files of their own rather than appending it to existing source code files
  • the approach to add watermark views in every UIViewController subclass that might show content has a number of drawbacks and weaknesses:
    • new views controllers can emerge where this is not thought of or can't be accounted for
    • new subclasses of already modified classes may/will add views on top
    • the approach generally fails if a modified view controller dynamically adds and removes views (as the Watermark will then no longer be the front most view and be covered by the views that were added after it)
    • the code changes spread to unrelated sourcecode, weakening modularity
  • that said, ThemeWindow (containing all view controllers) or especially BrowserNavigationViewController (containing all (content) view controllers) strike me as good, central places to apply a watermark without having to modify so many different view controllers and micromanage their views
  • ideally the watermark feature should be removable from the app by deleting a folder and otherwise removing 1-3 lines of code elsewhere

Other than that:

  • please run SwiftLint and correct the warnings in your code (there are many)
  • please read and accept the CLA

moreViewController.watermark(
username: core.bookmark.userName,
userMail: core.bookmark.user?.emailAddress
)
Copy link
Contributor

Choose a reason for hiding this comment

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

This and similar code is repeated several times across in different places. It would require changes if

  • the location of the extracted userName or emailAdress changes
  • the data that should be extracted from the Bookmark changes
    Passing the bookmark directly and extracting the desired information in the watermark method would be a more flexible and less repetitive pattern.

static let watermarkFontSize = OCClassSettingsKey("watermark-font-size")
static let watermarkText = OCClassSettingsKey("watermark-text")
static let watermarkShowMail = OCClassSettingsKey("watermark-show-mail")
static let watermarkShowDate = OCClassSettingsKey("watermark-show-date")
Copy link
Contributor

Choose a reason for hiding this comment

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

Since the watermark options here are so numerous, related and for a specific feature only, it would be good to move them to their own category (classSettingsIdentifier) and class rather than adding them to the broader VendorServices / app category.

.watermarkText: "",
.watermarkShowMail: true,
.watermarkShowDate: true,
.showScreenshotNotification: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a trailing comma here that should be removed.

@@ -17,6 +17,8 @@
*/

import UIKit
import ownCloudApp
Copy link
Contributor

Choose a reason for hiding this comment

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

This change is not needed and should be removed.

@@ -48,3 +49,273 @@ public extension UIViewController {
return true
}
}

public extension UIViewController {
func observeScreenshotEvent() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please take a different approach to observe for UIApplication.userDidTakeScreenshotNotification. Setting up and removing an observer in every view controller has several issues:

  • if a view controller already presents another view controller when this notification is observed, the alert will not be shown
  • there can (and likely will) be several active observers for screenshots, which could result in several alerts being shown for a single notification
  • the app also presents system-provided UIViewControllers, which, for this approach to work, would need all be subclassed and all calling sites be replaced. And even then, the system presenting additional view controllers of its own on those subclassed view controllers would still not be covered.
  • structurally, these methods have nothing to do with UIViewController and don't belong here.

A solution to all of the above would be to create a new class (f.ex. as a singleton), centrally managing the observation for UIApplication.userDidTakeScreenshotNotification and - if the event occurs - dynamically determining the front-most UIViewController and presenting the alert on that. See AppRootViewController.presentAlertAsCard() for an example on how to do that.

username: username,
userMail: userMail
)
view.addSubview(watermark)
Copy link
Contributor

Choose a reason for hiding this comment

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

This method creates and adds a new Watermark view to the UIViewController even if the watermark is not enabled. To avoid unnecessary usage of CPU and memory resources, this should not be done.

watermark.leadingAnchor.constraint(equalTo: view.leadingAnchor),
watermark.trailingAnchor.constraint(equalTo: view.trailingAnchor),
watermark.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
Copy link
Contributor

Choose a reason for hiding this comment

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

The whole segment of code with addSubview(), translatesAutoresizingMaskIntoConstraints and NSLayoutConstraint.activate can be replaced with a single line call of UIView.embed(toFillWith:).

username: String?,
userMail: String?
){
let watermark = Watermark(
Copy link
Contributor

Choose a reason for hiding this comment

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

To be consistent with the rest of the code, that variable should end with View, so f.ex. be called watermarkView.

class Watermark: UIView {

var angle: CGFloat = 45.0 {
didSet { updateView() }
Copy link
Contributor

Choose a reason for hiding this comment

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

All of the didSets here are superfluous as:

  • the setters are not called anywhere but from init(), from where didSet is generally not called
  • (if they were, the base class would not yet have been initialized - and even if it were, the size would be 0 x 0 and it wouldn't make sense to layout for that view size)

}
}

class Watermark: UIView {
Copy link
Contributor

Choose a reason for hiding this comment

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

This view is implemented by filling it with further UILabel views, which are manually removed, re-added and repositioned on every change, with change detection hinging on an - as you write yourself "not ideal" - method override of updateConstraints() with the inherent risk of causing issues like infinite loops or degraded performance in the future.

Possible solutions to this:

  • using the layout constraint system to layout the subviews (as in the rest of the app) automatically
  • overriding drawRect() and using the iOS graphics system to actually draw the strings in question

If watermarks are meant to be shown more densely, requiring more UILabels to be added as subviews, the latter option should be a lot more performant and - for that reason - would be my first choice for adding such a feature.

@DeepDiver1975
Copy link
Member

@blicknix rebase is also necessary as we just added GitHub workflows - THX

Copy link
Collaborator

@hosy hosy left a comment

Choose a reason for hiding this comment

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

Some general thoughts about this feature:

This feature isn’t secure if it’s possible to open files from third-party apps. For instance, if you can open the same file via File Provider with another app or Files.app and take a screenshot, this feature isn’t secure.

If this feature is enabled, I suggest not delivering files via File Provider or showing an error message if the user wants to open or copy the file with a hint. Instead, open the ownCloud app to view the file. Additionally, it shouldn’t be possible to share files from the app.

Another idea to consider is an option instead of watermarking, where you can completely hide the content or disallow taking screenshots (perhaps with an overlay view or DRM content view).

@hosy
Copy link
Collaborator

hosy commented Nov 13, 2024

@blicknix please add a detailed description about the MDM parameters to the PR and add meta data to the class settings.

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