Merge branch 'release/0.2.3'

This commit is contained in:
Bob Sun 2017-04-02 22:57:13 -07:00
commit ec987c115f
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
57 changed files with 1483 additions and 391 deletions

2
.gitignore vendored
View file

@ -62,7 +62,7 @@ Carthage/Build
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
fastlane
fastlane/README.md
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots

9
.travis.yml Normal file
View file

@ -0,0 +1,9 @@
language: objective-c
osx_image: xcode8.3
branches:
only:
- develop
before_install:
- echo -e "machine github.com\n login $GITHUB_ACCESS_TOKEN" >> ~/.netrc
script:
- ./fastlane/travis.sh

View file

@ -1,6 +1,6 @@
github "SVProgressHUD/SVProgressHUD"
github "radex/SwiftyUserDefaults"
github "libgit2/objective-git" "master"
github "libgit2/objective-git"
github "zahlz/SwiftPasscodeLock" "master"
github "bitserf/FavIcon"
github "kishikawakatsumi/KeychainAccess" "master"

View file

@ -4,6 +4,7 @@
[![GitHub release](https://img.shields.io/github/release/mssun/pass-ios.svg)](https://github.com/mssun/pass-ios/releases)
![Swift 3.1](https://img.shields.io/badge/Swift-3.1-orange.svg)
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/passforios/passforios)
[![Build Status](https://travis-ci.org/mssun/passforios.svg?branch=develop)](https://travis-ci.org/mssun/passforios)
Pass is an iOS client compatible with [ZX2C4's Pass command line
application](http://www.passwordstore.org/). It is a password manager using

4
fastlane/Appfile Normal file
View file

@ -0,0 +1,4 @@
app_identifier ENV["APP_IDENTIFIER"]
apple_id ENV["APPLE_ID"]
team_id ENV["TEAM_ID"]
itc_team_id ENV["ITC_TEAM_ID"]

101
fastlane/Fastfile Normal file
View file

@ -0,0 +1,101 @@
# Customise this file, documentation can be found here:
# https://github.com/fastlane/fastlane/tree/master/fastlane/docs
# All available actions: https://docs.fastlane.tools/actions
# can also be listed using the `fastlane actions` command
# Change the syntax highlighting to Ruby
# All lines starting with a # are ignored when running `fastlane`
# If you want to automatically update fastlane if a new version is available:
# update_fastlane
# This is the minimum version number required.
# Update this, if you use features of a newer version
fastlane_version "2.17.0"
default_platform :ios
platform :ios do
before_all do
cocoapods
carthage(platform: "iOS")
end
desc "Travis"
lane :travis do
test
beta
end
desc "Runs all the tests"
lane :test do
scan(
scheme: "pass",
device: "iPhone 7"
)
end
desc "Submit a new Beta Build to Apple TestFlight"
desc "This will also make sure the profile is up to date"
lane :beta do
create_keychain(
name: ENV["MATCH_KEYCHAIN_NAME"],
password: ENV["MATCH_KEYCHAIN_PASSWORD"],
default_keychain: true,
unlock: true,
timeout: 3600,
add_to_search_list: true
)
match(
type: "appstore",
keychain_name: ENV["MATCH_KEYCHAIN_NAME"],
keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"],
readonly: true
)
# ensure_git_status_clean
increment_build_number(
build_number: latest_testflight_build_number + 1,
xcodeproj: "pass.xcodeproj"
)
# commit_version_bump(xcodeproj: "pass.xcodeproj")
# push_to_git_remote
gym(scheme: "pass")
pilot(skip_waiting_for_build_processing: true)
mailgun(
postmaster: ENV["MY_POSTMASTER"],
apikey: ENV["MY_API_KEY"],
to: ENV["DESTINATION_EMAIL"],
from: ENV["EMAIL_FROM_NAME"],
reply_to: ENV["EMAIL_REPLY_TO"],
subject: "A new build of Pass for iOS has been uploaded",
success: true,
app_link: "https://github.com/mssun/passforios",
ci_build_link: "https://travis-ci.org/mssun/passforios",
message: "A new build has been uploaded by Travis CI.",
)
end
desc "Deploy a new version to the App Store"
lane :release do
# match(type: "appstore")
# snapshot
gym(scheme: "pass") # Build your app - more options available
deliver(force: true)
# frameit
end
# You can define as many lanes as you want
after_all do |lane|
end
error do |lane, exception|
end
end
# More information about multiple platforms in fastlane: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
# All available actions: https://docs.fastlane.tools/actions
# fastlane reports which actions are used
# No personal data is recorded. Learn more at https://github.com/fastlane/enhancer

9
fastlane/Matchfile Normal file
View file

@ -0,0 +1,9 @@
git_url "https://github.com/mssun/certificates-passforios.git"
type "development" # The default type, can be: appstore, adhoc, enterprise or development
# app_identifier ["tools.fastlane.app", "tools.fastlane.app2"]
# username "user@fastlane.tools" # Your Apple Developer Portal username
# For all available options run `fastlane match --help`
# Remove the # in the beginning of the line to enable the other options

9
fastlane/travis.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
gem update fastlane
if [ "$TRAVIS_PULL_REQUEST" == "true" ]; then
fastlane test;
else
fastlane travis;
fi
exit $?

View file

@ -24,6 +24,7 @@
DC037CBF1E4ED4E100609409 /* TextViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC037CBD1E4ED4E100609409 /* TextViewTableViewCell.swift */; };
DC037CC01E4ED4E100609409 /* TextViewTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC037CBE1E4ED4E100609409 /* TextViewTableViewCell.xib */; };
DC1208581E35EBE60042942E /* ObjectiveGit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC1208571E35EBE60042942E /* ObjectiveGit.framework */; };
DC13B1511E8640810097803F /* passTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC13B1501E8640810097803F /* passTests.swift */; };
DC193FFA1E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC193FF91E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift */; };
DC193FFC1E49E0340077E0A3 /* PasscodeLock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC193FFB1E49E0340077E0A3 /* PasscodeLock.framework */; };
DC193FFE1E49E0760077E0A3 /* PasscodeLockRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC193FFD1E49E0760077E0A3 /* PasscodeLockRepository.swift */; };
@ -52,10 +53,10 @@
DCC277D21E30D6EA00402246 /* pass.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DCC408C81E30BA1300F29B0E /* pass.xcdatamodeld */; };
DCC408A41E2FCC9E00F29B0E /* PasswordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC408A31E2FCC9E00F29B0E /* PasswordStore.swift */; };
DCC408C71E307DBB00F29B0E /* SVProgressHUD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */; };
DCC441521E8F6C06008A90C4 /* RawPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */; };
DCC441541E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */; };
DCDDEAB01E4639F300F68193 /* LabelTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DCDDEAAF1E4639F300F68193 /* LabelTableViewCell.xib */; };
DCDDEAB31E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDDEAB11E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift */; };
DCE6C2671E71261C003038C6 /* PasswordWithFolderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6C2651E71261C003038C6 /* PasswordWithFolderTableViewCell.swift */; };
DCE6C2681E71261C003038C6 /* PasswordWithFolderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DCE6C2661E71261C003038C6 /* PasswordWithFolderTableViewCell.xib */; };
DCFB779A1E4F3BCF008DE471 /* TitleTextFieldTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCFB77981E4F3BCF008DE471 /* TitleTextFieldTableViewCell.swift */; };
DCFB779B1E4F3BCF008DE471 /* TitleTextFieldTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DCFB77991E4F3BCF008DE471 /* TitleTextFieldTableViewCell.xib */; };
DCFB779E1E4F40C7008DE471 /* FillPasswordTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCFB779C1E4F40C7008DE471 /* FillPasswordTableViewCell.swift */; };
@ -67,6 +68,16 @@
DCFB77AB1E503729008DE471 /* ContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCFB77AA1E503729008DE471 /* ContentTableViewCell.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
DC13B1531E8640810097803F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DC917BCB1E2E8231000FDF54 /* Project object */;
proxyType = 1;
remoteGlobalIDString = DC917BD21E2E8231000FDF54;
remoteInfo = pass;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
274CCFCF32444A2FF46BE7F4 /* Pods-pass.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-pass.debug.xcconfig"; path = "Pods/Target Support Files/Pods-pass/Pods-pass.debug.xcconfig"; sourceTree = "<group>"; };
A262A58C1E68749C006B0890 /* Base32.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Base32.framework; path = Carthage/Build/iOS/Base32.framework; sourceTree = "<group>"; };
@ -87,6 +98,9 @@
DC037CBD1E4ED4E100609409 /* TextViewTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextViewTableViewCell.swift; sourceTree = "<group>"; };
DC037CBE1E4ED4E100609409 /* TextViewTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextViewTableViewCell.xib; sourceTree = "<group>"; };
DC1208571E35EBE60042942E /* ObjectiveGit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ObjectiveGit.framework; path = Carthage/Build/iOS/ObjectiveGit.framework; sourceTree = "<group>"; };
DC13B14E1E8640810097803F /* passTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = passTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
DC13B1501E8640810097803F /* passTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = passTests.swift; sourceTree = "<group>"; };
DC13B1521E8640810097803F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DC193FF91E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsTableViewController.swift; sourceTree = "<group>"; };
DC193FFB1E49E0340077E0A3 /* PasscodeLock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PasscodeLock.framework; path = Carthage/Build/iOS/PasscodeLock.framework; sourceTree = "<group>"; };
DC193FFD1E49E0760077E0A3 /* PasscodeLockRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeLockRepository.swift; sourceTree = "<group>"; };
@ -118,10 +132,10 @@
DCC408A31E2FCC9E00F29B0E /* PasswordStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordStore.swift; sourceTree = "<group>"; };
DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = "<group>"; };
DCC408C91E30BA1300F29B0E /* pass.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = pass.xcdatamodel; sourceTree = "<group>"; };
DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawPasswordViewController.swift; sourceTree = "<group>"; };
DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitSSHKeyArmorSettingTableViewController.swift; sourceTree = "<group>"; };
DCDDEAAF1E4639F300F68193 /* LabelTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LabelTableViewCell.xib; sourceTree = "<group>"; };
DCDDEAB11E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordDetailTitleTableViewCell.swift; sourceTree = "<group>"; };
DCE6C2651E71261C003038C6 /* PasswordWithFolderTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordWithFolderTableViewCell.swift; sourceTree = "<group>"; };
DCE6C2661E71261C003038C6 /* PasswordWithFolderTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PasswordWithFolderTableViewCell.xib; sourceTree = "<group>"; };
DCFB77981E4F3BCF008DE471 /* TitleTextFieldTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TitleTextFieldTableViewCell.swift; sourceTree = "<group>"; };
DCFB77991E4F3BCF008DE471 /* TitleTextFieldTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TitleTextFieldTableViewCell.xib; sourceTree = "<group>"; };
DCFB779C1E4F40C7008DE471 /* FillPasswordTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FillPasswordTableViewCell.swift; sourceTree = "<group>"; };
@ -134,6 +148,13 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
DC13B14B1E8640810097803F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DC917BD01E2E8231000FDF54 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -162,6 +183,15 @@
name = Pods;
sourceTree = "<group>";
};
DC13B14F1E8640810097803F /* passTests */ = {
isa = PBXGroup;
children = (
DC13B1501E8640810097803F /* passTests.swift */,
DC13B1521E8640810097803F /* Info.plist */,
);
path = passTests;
sourceTree = "<group>";
};
DC19400C1E4B39400077E0A3 /* Controllers */ = {
isa = PBXGroup;
children = (
@ -177,12 +207,14 @@
DCA049991E335CC800522E8F /* GitServerSettingTableViewController.swift */,
DC037CA51E4B883900609409 /* OpenSourceComponentsTableViewController.swift */,
DC4914981E434600007FF592 /* PasswordDetailTableViewController.swift */,
DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */,
DCFB77A81E502FF6008DE471 /* PasswordEditorTableViewController.swift */,
DC5734AD1E439AD400D09270 /* PasswordsViewController.swift */,
DCA0499B1E3362F400522E8F /* PGPKeySettingTableViewController.swift */,
DCAAF7441E2FA66800AB94BC /* SettingsTableViewController.swift */,
DC037CA91E4B8EAE00609409 /* SpecialThanksTableViewController.swift */,
DC8963BF1E38EEB900828B09 /* SSHKeySettingTableViewController.swift */,
DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */,
);
path = Controllers;
sourceTree = "<group>";
@ -229,8 +261,6 @@
DC037CBD1E4ED4E100609409 /* TextViewTableViewCell.swift */,
DC037CBE1E4ED4E100609409 /* TextViewTableViewCell.xib */,
DCFB77AA1E503729008DE471 /* ContentTableViewCell.swift */,
DCE6C2651E71261C003038C6 /* PasswordWithFolderTableViewCell.swift */,
DCE6C2661E71261C003038C6 /* PasswordWithFolderTableViewCell.xib */,
);
path = Views;
sourceTree = "<group>";
@ -239,6 +269,7 @@
isa = PBXGroup;
children = (
DC917BD51E2E8231000FDF54 /* pass */,
DC13B14F1E8640810097803F /* passTests */,
DC917BD41E2E8231000FDF54 /* Products */,
DC917BED1E2F38C4000FDF54 /* Frameworks */,
A51B01737D08DB47BB58F85A /* Pods */,
@ -249,6 +280,7 @@
isa = PBXGroup;
children = (
DC917BD31E2E8231000FDF54 /* pass.app */,
DC13B14E1E8640810097803F /* passTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -289,6 +321,24 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
DC13B14D1E8640810097803F /* passTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = DC13B1571E8640810097803F /* Build configuration list for PBXNativeTarget "passTests" */;
buildPhases = (
DC13B14A1E8640810097803F /* Sources */,
DC13B14B1E8640810097803F /* Frameworks */,
DC13B14C1E8640810097803F /* Resources */,
);
buildRules = (
);
dependencies = (
DC13B1541E8640810097803F /* PBXTargetDependency */,
);
name = passTests;
productName = passTests;
productReference = DC13B14E1E8640810097803F /* passTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
DC917BD21E2E8231000FDF54 /* pass */ = {
isa = PBXNativeTarget;
buildConfigurationList = DC917BE51E2E8231000FDF54 /* Build configuration list for PBXNativeTarget "pass" */;
@ -316,14 +366,20 @@
DC917BCB1E2E8231000FDF54 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0820;
LastSwiftUpdateCheck = 0830;
LastUpgradeCheck = 0820;
ORGANIZATIONNAME = "Bob Sun";
TargetAttributes = {
DC13B14D1E8640810097803F = {
CreatedOnToolsVersion = 8.3;
DevelopmentTeam = 4WDM8E95VU;
ProvisioningStyle = Automatic;
TestTargetID = DC917BD21E2E8231000FDF54;
};
DC917BD21E2E8231000FDF54 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = 4WDM8E95VU;
ProvisioningStyle = Automatic;
ProvisioningStyle = Manual;
};
};
};
@ -341,11 +397,19 @@
projectRoot = "";
targets = (
DC917BD21E2E8231000FDF54 /* pass */,
DC13B14D1E8640810097803F /* passTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
DC13B14C1E8640810097803F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
DC917BD11E2E8231000FDF54 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@ -357,7 +421,6 @@
DCDDEAB01E4639F300F68193 /* LabelTableViewCell.xib in Resources */,
DC917BDC1E2E8231000FDF54 /* Main.storyboard in Resources */,
A2802BFA1E70813A00879216 /* SliderTableViewCell.xib in Resources */,
DCE6C2681E71261C003038C6 /* PasswordWithFolderTableViewCell.xib in Resources */,
DCFB779B1E4F3BCF008DE471 /* TitleTextFieldTableViewCell.xib in Resources */,
DCFB779F1E4F40C7008DE471 /* FillPasswordTableViewCell.xib in Resources */,
DC037CC01E4ED4E100609409 /* TextViewTableViewCell.xib in Resources */,
@ -437,12 +500,21 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
DC13B14A1E8640810097803F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DC13B1511E8640810097803F /* passTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
DC917BCF1E2E8231000FDF54 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DCC408A41E2FCC9E00F29B0E /* PasswordStore.swift in Sources */,
DC037CBF1E4ED4E100609409 /* TextViewTableViewCell.swift in Sources */,
DCC441541E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift in Sources */,
DC8963C01E38EEB900828B09 /* SSHKeySettingTableViewController.swift in Sources */,
DC193FFA1E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift in Sources */,
DCFB77AB1E503729008DE471 /* ContentTableViewCell.swift in Sources */,
@ -452,7 +524,6 @@
DC5F385B1E56AADB00C69ACA /* PGPKeyArmorSettingTableViewController.swift in Sources */,
A27424D91E7C35960093F436 /* NotificationNames.swift in Sources */,
DCAAF7451E2FA66800AB94BC /* SettingsTableViewController.swift in Sources */,
DCE6C2671E71261C003038C6 /* PasswordWithFolderTableViewCell.swift in Sources */,
DCFB77A71E502DF9008DE471 /* EditPasswordTableViewController.swift in Sources */,
DCA0499A1E335CC800522E8F /* GitServerSettingTableViewController.swift in Sources */,
DCDDEAB31E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift in Sources */,
@ -469,6 +540,7 @@
DC037CB21E4CAB1700609409 /* AboutRepositoryTableViewController.swift in Sources */,
DC037CB01E4CA51F00609409 /* GeneralSettingsTableViewController.swift in Sources */,
DC037CB81E4DD1A500609409 /* AddPasswordTableViewController.swift in Sources */,
DCC441521E8F6C06008A90C4 /* RawPasswordViewController.swift in Sources */,
DC1940001E49E1A60077E0A3 /* PasscodeLockConfiguration.swift in Sources */,
DC917BD71E2E8231000FDF54 /* AppDelegate.swift in Sources */,
DCFB779A1E4F3BCF008DE471 /* TitleTextFieldTableViewCell.swift in Sources */,
@ -484,6 +556,14 @@
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
DC13B1541E8640810097803F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DC917BD21E2E8231000FDF54 /* pass */;
targetProxy = DC13B1531E8640810097803F /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
DC917BDA1E2E8231000FDF54 /* Main.storyboard */ = {
isa = PBXVariantGroup;
@ -504,6 +584,38 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
DC13B1551E8640810097803F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
DEVELOPMENT_TEAM = 4WDM8E95VU;
INFOPLIST_FILE = passTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pass.app/pass";
};
name = Debug;
};
DC13B1561E8640810097803F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
DEVELOPMENT_TEAM = 4WDM8E95VU;
INFOPLIST_FILE = passTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pass.app/pass";
};
name = Release;
};
DC917BE31E2E8231000FDF54 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -601,7 +713,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 24;
CURRENT_PROJECT_VERSION = 2;
DEFINES_MODULE = NO;
DEVELOPMENT_TEAM = 4WDM8E95VU;
FRAMEWORK_SEARCH_PATHS = (
@ -620,6 +732,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "893c10b3-79b1-46f7-914a-e625bf10d665";
PROVISIONING_PROFILE_SPECIFIER = "match Development me.mssun.passforios";
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/pass/Helpers/Objective-CBridgingHeader.h";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -632,8 +746,8 @@
baseConfigurationReference = AEAD6B31EAF5D061447A68CC /* Pods-pass.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 24;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CURRENT_PROJECT_VERSION = 2;
DEFINES_MODULE = NO;
DEVELOPMENT_TEAM = 4WDM8E95VU;
FRAMEWORK_SEARCH_PATHS = (
@ -652,6 +766,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "aefeea85-1194-4db2-9ce4-fb9995e2fdff";
PROVISIONING_PROFILE_SPECIFIER = "match AppStore me.mssun.passforios";
SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/pass/Helpers/Objective-CBridgingHeader.h";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -662,6 +778,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
DC13B1571E8640810097803F /* Build configuration list for PBXNativeTarget "passTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DC13B1551E8640810097803F /* Debug */,
DC13B1561E8640810097803F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DC917BCE1E2E8231000FDF54 /* Build configuration list for PBXProject "pass" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View file

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
BuildableName = "pass.app"
BlueprintName = "pass"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC13B14D1E8640810097803F"
BuildableName = "passTests.xctest"
BlueprintName = "passTests"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
BuildableName = "pass.app"
BlueprintName = "pass"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
BuildableName = "pass.app"
BlueprintName = "pass"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "OS_ACTIVITY_MODE"
value = "disable"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
BuildableName = "pass.app"
BlueprintName = "pass"
ReferencedContainer = "container:pass.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Screenshot-50.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Screenshot-100.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

View file

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Copy-50.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Copy-100.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

View file

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Horizontal Settings Mixer-50.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Horizontal Settings Mixer-75.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "not-visible-interface-symbol-of-an-eye-with-a-slash-on-it.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Refresh-64.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Refresh-75.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "eye-visible-outlined-interface-symbol.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16F43c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
<device id="retina5_5" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12074.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -158,7 +158,7 @@
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="PASSCODE" id="Cx7-e4-wCO">
<tableViewSection headerTitle="SECURITY" id="Cx7-e4-wCO">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="passcodeTableViewCell" textLabel="RaZ-6t-0CU" detailTextLabel="HXb-ZX-HUv" style="IBUITableViewCellStyleValue1" id="6Y0-mj-qhA">
<rect key="frame" x="0.0" y="223" width="414" height="44"/>
@ -167,8 +167,8 @@
<rect key="frame" x="0.0" y="0.0" width="381" height="43.666666666666664"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Passcode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RaZ-6t-0CU">
<rect key="frame" x="15" y="11.999999999999998" width="74.333333333333329" height="20.333333333333332"/>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Passcode Lock" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RaZ-6t-0CU">
<rect key="frame" x="15" y="11.999999999999998" width="115" height="20.333333333333332"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
@ -288,7 +288,7 @@
<rect key="frame" x="0.0" y="28" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="OfB-1N-1Am" id="fh0-au-C6q">
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" tag="101" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="00/00/0000" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hj4-EP-LFW">
@ -340,10 +340,10 @@
<tableViewSection headerTitle="Git Repository URL" id="pbe-W6-w4V">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="gitRepositoryURLTabelViewCell" rowHeight="52" id="FRr-pf-aPO">
<rect key="frame" x="0.0" y="55" width="414" height="52"/>
<rect key="frame" x="0.0" y="55.5" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="FRr-pf-aPO" id="60A-PS-qGe">
<rect key="frame" x="0.0" y="0.0" width="414" height="51"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Git Repository URL" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="EVT-VU-sCi">
@ -366,10 +366,10 @@
<tableViewSection headerTitle="Username" id="fRu-A2-SCk">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="usernameTableVIewCell" rowHeight="52" id="tnj-5U-kMB">
<rect key="frame" x="0.0" y="163" width="414" height="52"/>
<rect key="frame" x="0.0" y="163.5" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="tnj-5U-kMB" id="f0c-pI-MSJ">
<rect key="frame" x="0.0" y="0.0" width="414" height="51"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Username" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="TMg-Gk-7nG">
@ -392,10 +392,10 @@
<tableViewSection headerTitle="Authentication Method" id="h0N-tI-shZ">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="KrP-nb-haa">
<rect key="frame" x="0.0" y="271" width="414" height="44"/>
<rect key="frame" x="0.0" y="271.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KrP-nb-haa" id="1uB-oE-kfI">
<rect key="frame" x="0.0" y="0.0" width="414" height="43"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LfQ-Af-j2O">
@ -423,10 +423,10 @@
<inset key="separatorInset" minX="62" minY="0.0" maxX="0.0" maxY="0.0"/>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" accessoryType="detailButton" hidesAccessoryWhenEditing="NO" indentationLevel="2" indentationWidth="0.0" shouldIndentWhileEditing="NO" id="Qmt-bo-CuJ">
<rect key="frame" x="0.0" y="315" width="414" height="44"/>
<rect key="frame" x="0.0" y="315.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Qmt-bo-CuJ" id="p3u-8b-h3U">
<rect key="frame" x="0.0" y="0.0" width="367" height="43"/>
<rect key="frame" x="0.0" y="0.0" width="367" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SSH Key" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ezz-76-a53">
@ -477,10 +477,11 @@
<connections>
<outlet property="authPasswordCell" destination="KrP-nb-haa" id="zjH-sg-dfJ"/>
<outlet property="authSSHKeyCell" destination="Qmt-bo-CuJ" id="11L-Tt-MgM"/>
<outlet property="gitRepositoryURLTextField" destination="EVT-VU-sCi" id="2EH-Uq-tbU"/>
<outlet property="gitURLTextField" destination="EVT-VU-sCi" id="XdU-3l-Nsv"/>
<outlet property="usernameTextField" destination="TMg-Gk-7nG" id="htL-4C-WJF"/>
<segue destination="7K9-cE-9qq" kind="unwind" identifier="saveGitServerSettingSegue" unwindAction="saveGitServerSettingWithSegue:" id="5UN-sC-xCA"/>
<segue destination="hqC-Ic-NMi" kind="show" identifier="showSSHKeySettingSegue" id="AP7-FV-2Ow"/>
<segue destination="hqC-Ic-NMi" kind="show" identifier="setGitSSHKeyByURLSegue" id="AP7-FV-2Ow"/>
<segue destination="WgM-cY-mig" kind="show" identifier="setGitSSHKeyByArmorSegue" id="HLm-0q-hUg"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7c1-c7-Qyp" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -503,7 +504,7 @@
<rect key="frame" x="0.0" y="35" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="BYZ-9g-xZy" id="Zfn-rK-sN1">
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Public Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dWi-eh-7Eq">
@ -533,7 +534,7 @@
<rect key="frame" x="0.0" y="87" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="vpk-J8-j7t" id="1td-qT-6ts">
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Private Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Qht-RC-Yeg">
@ -626,7 +627,7 @@
<scene sceneID="9wY-d0-fB1">
<objects>
<tableViewController id="tW4-E9-CGv" customClass="PasswordDetailTableViewController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="hfi-6O-04U">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="hfi-6O-04U">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
@ -638,6 +639,7 @@
</tableView>
<connections>
<segue destination="eui-w4-P7T" kind="show" identifier="editPasswordSegue" id="Ydg-pZ-oNS"/>
<segue destination="kNf-lG-BeC" kind="presentation" identifier="showRawPasswordSegue" modalTransitionStyle="flipHorizontal" id="qpR-3X-eyt"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="9sC-44-OKL" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -736,6 +738,50 @@
</objects>
<point key="canvasLocation" x="6084" y="601"/>
</scene>
<!--Raw Password View Controller-->
<scene sceneID="Oqg-Vh-EYD">
<objects>
<viewController id="mlB-OK-hLo" customClass="RawPasswordViewController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="XeK-cv-AXf"/>
<viewControllerLayoutGuide type="bottom" id="7JD-uM-IyS"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="5YZ-6i-LST">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3dt-Ph-4As">
<rect key="frame" x="20" y="4" width="374" height="732"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<fontDescription key="fontDescription" name="Courier" family="Courier" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="3dt-Ph-4As" secondAttribute="trailing" id="2IV-7r-9eG"/>
<constraint firstItem="7JD-uM-IyS" firstAttribute="top" secondItem="3dt-Ph-4As" secondAttribute="bottom" id="3yp-aL-exH"/>
<constraint firstItem="3dt-Ph-4As" firstAttribute="top" secondItem="XeK-cv-AXf" secondAttribute="bottom" constant="-60" id="R2h-nh-8eZ"/>
<constraint firstAttribute="leadingMargin" secondItem="3dt-Ph-4As" secondAttribute="leading" id="jgf-j1-lNa"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="c13-zM-tLf">
<barButtonItem key="leftBarButtonItem" title="Back" id="9yM-Mg-Cg8">
<connections>
<segue destination="pNY-ul-yzB" kind="unwind" unwindAction="backWithSegue:" id="SzI-Au-9rJ"/>
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="rawPasswordTextView" destination="3dt-Ph-4As" id="SVL-F8-5yD"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="zgb-Ji-ZBp" userLabel="First Responder" sceneMemberID="firstResponder"/>
<exit id="pNY-ul-yzB" userLabel="Exit" sceneMemberID="exit"/>
</objects>
<point key="canvasLocation" x="7721.739130434783" y="-24.456521739130437"/>
</scene>
<!--About Repository Table View Controller-->
<scene sceneID="u1v-BP-jaG">
<objects>
@ -791,7 +837,7 @@
<rect key="frame" x="0.0" y="35" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Jy1-4S-Lvf" id="tJE-ww-okf">
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Public Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oys-xP-ZrB">
@ -821,7 +867,7 @@
<rect key="frame" x="0.0" y="87" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="MA5-lE-8dT" id="pTv-Wj-psC">
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Private Key URL" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="C2w-dd-roS">
@ -847,36 +893,6 @@
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="passphrase" rowHeight="52" id="yER-vT-YTO">
<rect key="frame" x="0.0" y="139" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="yER-vT-YTO" id="Mip-zw-xLu">
<rect key="frame" x="0.0" y="0.0" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Passphrase" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Rnj-2x-ksO">
<rect key="frame" x="15" y="8" width="391" height="15"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Passphrase" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="4oZ-lc-EFS">
<rect key="frame" x="15" y="23" width="391" height="21"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="URL" secureTextEntry="YES"/>
</textField>
</subviews>
<constraints>
<constraint firstItem="Rnj-2x-ksO" firstAttribute="leading" secondItem="Mip-zw-xLu" secondAttribute="leadingMargin" constant="7" id="B0m-eX-UKn"/>
<constraint firstItem="4oZ-lc-EFS" firstAttribute="top" secondItem="Rnj-2x-ksO" secondAttribute="bottom" id="V7l-fu-KnP"/>
<constraint firstAttribute="trailingMargin" secondItem="Rnj-2x-ksO" secondAttribute="trailing" id="nEm-LM-Uwg"/>
<constraint firstAttribute="topMargin" secondItem="Rnj-2x-ksO" secondAttribute="top" id="nPH-l3-bRA"/>
<constraint firstAttribute="trailingMargin" secondItem="4oZ-lc-EFS" secondAttribute="trailing" id="uIe-w7-hcX"/>
<constraint firstItem="4oZ-lc-EFS" firstAttribute="leading" secondItem="Rnj-2x-ksO" secondAttribute="leading" id="vEc-EI-F0z"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
@ -886,14 +902,13 @@
</connections>
</tableView>
<connections>
<outlet property="passphraseTextField" destination="4oZ-lc-EFS" id="dtf-H3-gBh"/>
<outlet property="privateKeyURLTextField" destination="4iJ-oB-R1f" id="scx-lz-dUW"/>
<outlet property="publicKeyURLTextField" destination="Q1j-Z4-Ae8" id="nfA-W3-kJ2"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="eY3-aM-BJB" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="5132" y="2893"/>
<point key="canvasLocation" x="7433" y="2204"/>
</scene>
<!--General Settings Table View Controller-->
<scene sceneID="fho-xr-1Ah">
@ -1135,7 +1150,7 @@
<rect key="frame" x="0.0" y="35" width="414" height="170"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Pv0-ev-stj" id="ywz-II-W1g">
<rect key="frame" x="0.0" y="0.0" width="414" height="170"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="169.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" delaysContentTouches="NO" canCancelContentTouches="NO" bouncesZoom="NO" editable="NO" usesAttributedText="YES" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xzQ-5d-kdL">
@ -1193,7 +1208,7 @@
<rect key="frame" x="0.0" y="261" width="414" height="160"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Lom-iT-l16" id="eya-Tv-r0q">
<rect key="frame" x="0.0" y="0.0" width="414" height="160"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oyB-oI-1fS">
@ -1249,7 +1264,7 @@ pfZ36xQbOAQYKKf6ZTT5R/Y=
<rect key="frame" x="0.0" y="477" width="414" height="160"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="J8U-ev-FRQ" id="eb0-vb-Fcc">
<rect key="frame" x="0.0" y="0.0" width="414" height="160"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lrQ-Ln-ZOv">
@ -1326,7 +1341,7 @@ pfZ36xQbOAQYKKf6ZTT5R/Y=
<placeholder placeholderIdentifier="IBFirstResponder" id="2Wn-7D-LLj" userLabel="First Responder" sceneMemberID="firstResponder"/>
<exit id="Ul9-vk-jhw" userLabel="Exit" sceneMemberID="exit"/>
</objects>
<point key="canvasLocation" x="6039.130434782609" y="4307.608695652174"/>
<point key="canvasLocation" x="7094" y="4030"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="m18-ch-m92">
@ -1346,6 +1361,218 @@ pfZ36xQbOAQYKKf6ZTT5R/Y=
</objects>
<point key="canvasLocation" x="5131.884057971015" y="4307.608695652174"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="Ps2-MC-JQX">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="kNf-lG-BeC" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="Dx6-8j-IzX">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="mlB-OK-hLo" kind="relationship" relationship="rootViewController" id="wp3-xi-lwY"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ycf-Zv-ej8" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="6720" y="-24"/>
</scene>
<!--GitSSH Key Armor Setting Table View Controller-->
<scene sceneID="zWR-BT-dXv">
<objects>
<tableViewController id="WgM-cY-mig" customClass="GitSSHKeyArmorSettingTableViewController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="e2Q-qC-LUk">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection id="HgL-ju-8xi">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="pgpKeyURLTableViewCell" rowHeight="170" id="EMz-nP-2Om">
<rect key="frame" x="0.0" y="35" width="414" height="170"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="EMz-nP-2Om" id="XW5-mL-PRC">
<rect key="frame" x="0.0" y="0.0" width="414" height="169.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" delaysContentTouches="NO" canCancelContentTouches="NO" bouncesZoom="NO" editable="NO" usesAttributedText="YES" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vkU-ka-wAm">
<rect key="frame" x="15" y="22" width="391" height="146.66666666666666"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<attributedString key="attributedText">
<fragment>
<string key="content">ASCII-armored key format similar to uuencoded documents rather than binary format.
</string>
<attributes>
<color key="NSColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name=".AppleSystemUIFont"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content"> $ cat ~/.ssh/id_rsa.pub
$ cat ~/.ssh/id_rsa</string>
<attributes>
<color key="NSColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="13" name="Menlo-Regular"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
</attributedString>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no"/>
</textView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ASCII-Armor Keys" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HIF-Zn-aBt">
<rect key="frame" x="15" y="8" width="391" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="Sh8-Aj-s7i"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="HIF-Zn-aBt" firstAttribute="leading" secondItem="XW5-mL-PRC" secondAttribute="leadingMargin" constant="7" id="2fZ-Tu-6jx"/>
<constraint firstAttribute="trailingMargin" secondItem="vkU-ka-wAm" secondAttribute="trailing" id="I1w-xW-439"/>
<constraint firstItem="vkU-ka-wAm" firstAttribute="top" secondItem="HIF-Zn-aBt" secondAttribute="bottom" constant="-7" id="QEU-iz-4AS"/>
<constraint firstItem="vkU-ka-wAm" firstAttribute="leading" secondItem="XW5-mL-PRC" secondAttribute="leadingMargin" constant="7" id="UEQ-vW-A0q"/>
<constraint firstAttribute="trailingMargin" secondItem="HIF-Zn-aBt" secondAttribute="trailing" id="h7g-Ft-YVp"/>
<constraint firstAttribute="bottomMargin" secondItem="vkU-ka-wAm" secondAttribute="bottom" constant="-7" id="lHv-ws-fct"/>
<constraint firstAttribute="topMargin" secondItem="HIF-Zn-aBt" secondAttribute="top" id="uqY-0s-I2H"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="ASCII-Armor Encrypted Public Key" id="w4D-Mh-0pP">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="pgpKeyURLTableViewCell" rowHeight="160" id="3po-uS-Nch">
<rect key="frame" x="0.0" y="261" width="414" height="160"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3po-uS-Nch" id="pet-if-EHU">
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Y9t-DD-6uH">
<rect key="frame" x="15" y="8" width="391" height="143.66666666666666"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<string key="text">-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFiiqFoBCACzvPOW+J1RlQOI9oB7Iwuk92rzpkKGC8tiwo4otHMc3rxmzYlG
gEdHmSi6Ix0+hYZ2vSWJamPQl9WUiYSBQmkVwoqiwXNWcqRePOylcqsj58owIxZt
23nXUIG78t+5BatoWyl9/qXsMUMSink7RAlOnjCyvoSJw9yzUVeWn723rvAr7ypV
yqjce4gOGIfGT8PpGXThDQjvg4gM+wxUD3+oNhW50Qb+crMsefxwEoGXbCA4+6ZO
nJGy2MhRr4FkXwNAAcvTvTmYXQfH8Q8xCtgl4PyxQA4OAeoEfxl5FOuOGIbNKgMs
133n2oue5dy3YERN2t9/Di8Iov4Bsc+nj6+vABEBAAG0E1Bhc3MgZm9yIGlPUyAo
RGVtbymJATcEEwEKACEFAliiqFoCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AA
CgkQl/oT92AAdt0TqAgAlTsGLTe9+qY9pfyBVpDk+z1SC9kaOYsvoj7hSWVOXxwP
yn4jxjHUr1C9fyaZqP9K4yIdl9jsGBkpaR6lhnUm9x5RO5gEAAAaLhx3ZgE7/XQg
y7WHibxsnLaTEMVnNh8BJzQveYSqO8frBkQofj0K9LNiqWHhcllKKNkVHqyeMRII
KYPOXvDnsnKqywapu5VyyErMnPqq6h7i3Z7+xDfVk8iUew9i0LR/QEmJO7iTH98C
DczQo+4VnflBIHiNcMaXQqC2Li5rS+O3a9hq5V/gJwBtIx1iXvDaRATH4aUANxTZ
POHVLwxBuTwilhnEaAPGkGu+oG1XF+RWOyrGYluWc7kBDQRYoqhaAQgAoh9B+sY1
jZZgnqP11a9wkjom2DcBJLreYNzgBFYhUcmxtq+ZWyorPFzMJpbIjzMGRiGWFIus
DE5D5LZdAvkINWKsbVN6Y3OI6Tj+/O45SuJ7EusG8Z1bwmjJqHy/Y//Fk0rF6ZFs
g0+p12Xzr5k8sMKUU4I5T4DNrsS+mwnOPItC7NGawkArPD7Mew0ma+Luv7b3g0gA
DuofvxKiN0jq6W9QPUNRY4BKm+Y03CdZNXyMDs/NTguDdfarawTIAIlJOSg5rV7S
74v73dtZkB5LySX1rBomRb5zkCg6/Ygap4RO1aO9gbXyPmc2lUQSBAJCS9d8h0qD
sNGembqhGDwHpQARAQABiQEfBBgBCgAJBQJYoqhaAhsMAAoJEJf6E/dgAHbdKl4H
/A/Q+hOh40M2B6Sh/Rsus+k1VL51+UXVJRyiNiqvofamufF5ULfeq3Pt5HfqpCsF
/A7iRP0MuanKcZbLDjDv6wWDuRvW9Kwn5qjbb6iBPh+wfyReqNMWGksJTyap0SNR
+Mn+sjbxGkUPjzVdRdqtw64R3DLW6SpMNIUVC6paazCViEtHsSauWwP/0+DRA/6j
xBHSAr0UaLsRTHleOSWV6ABrh9skJRWLBcOoi5pxotBs/3BmjqERQf9bwmmyxj2K
cE60Muv6qY4/KCQqPKhyFjJwXhS/e7BwxHEt1SbUb1aC4lH5UrbW9KYLu5lHJRdY
pfZ36xQbOAQYKKf6ZTT5R/Y=
=2pJ8
-----END PGP PUBLIC KEY BLOCK-----</string>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="8.5"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no"/>
</textView>
</subviews>
<constraints>
<constraint firstAttribute="bottomMargin" secondItem="Y9t-DD-6uH" secondAttribute="bottom" id="EAK-Wn-RP8"/>
<constraint firstItem="Y9t-DD-6uH" firstAttribute="top" secondItem="pet-if-EHU" secondAttribute="topMargin" id="LW7-SZ-CP3"/>
<constraint firstItem="Y9t-DD-6uH" firstAttribute="leading" secondItem="pet-if-EHU" secondAttribute="leadingMargin" constant="7" id="bw3-EI-8oh"/>
<constraint firstAttribute="trailingMargin" secondItem="Y9t-DD-6uH" secondAttribute="trailing" id="fCY-XN-Wtz"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="ASCII-ARMOR ENCRYPTED PRIVATE KEY" id="sxk-Yb-Y3x">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="pgpKeyURLTableViewCell" rowHeight="160" id="nmc-vy-Ab5">
<rect key="frame" x="0.0" y="477" width="414" height="160"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="nmc-vy-Ab5" id="TQD-GC-YOY">
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="23o-MP-wQY">
<rect key="frame" x="15" y="8" width="391" height="144"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<string key="text">-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFiiqFoBCACzvPOW+J1RlQOI9oB7Iwuk92rzpkKGC8tiwo4otHMc3rxmzYlG
gEdHmSi6Ix0+hYZ2vSWJamPQl9WUiYSBQmkVwoqiwXNWcqRePOylcqsj58owIxZt
23nXUIG78t+5BatoWyl9/qXsMUMSink7RAlOnjCyvoSJw9yzUVeWn723rvAr7ypV
yqjce4gOGIfGT8PpGXThDQjvg4gM+wxUD3+oNhW50Qb+crMsefxwEoGXbCA4+6ZO
nJGy2MhRr4FkXwNAAcvTvTmYXQfH8Q8xCtgl4PyxQA4OAeoEfxl5FOuOGIbNKgMs
133n2oue5dy3YERN2t9/Di8Iov4Bsc+nj6+vABEBAAG0E1Bhc3MgZm9yIGlPUyAo
RGVtbymJATcEEwEKACEFAliiqFoCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AA
CgkQl/oT92AAdt0TqAgAlTsGLTe9+qY9pfyBVpDk+z1SC9kaOYsvoj7hSWVOXxwP
yn4jxjHUr1C9fyaZqP9K4yIdl9jsGBkpaR6lhnUm9x5RO5gEAAAaLhx3ZgE7/XQg
y7WHibxsnLaTEMVnNh8BJzQveYSqO8frBkQofj0K9LNiqWHhcllKKNkVHqyeMRII
KYPOXvDnsnKqywapu5VyyErMnPqq6h7i3Z7+xDfVk8iUew9i0LR/QEmJO7iTH98C
DczQo+4VnflBIHiNcMaXQqC2Li5rS+O3a9hq5V/gJwBtIx1iXvDaRATH4aUANxTZ
POHVLwxBuTwilhnEaAPGkGu+oG1XF+RWOyrGYluWc7kBDQRYoqhaAQgAoh9B+sY1
jZZgnqP11a9wkjom2DcBJLreYNzgBFYhUcmxtq+ZWyorPFzMJpbIjzMGRiGWFIus
DE5D5LZdAvkINWKsbVN6Y3OI6Tj+/O45SuJ7EusG8Z1bwmjJqHy/Y//Fk0rF6ZFs
g0+p12Xzr5k8sMKUU4I5T4DNrsS+mwnOPItC7NGawkArPD7Mew0ma+Luv7b3g0gA
DuofvxKiN0jq6W9QPUNRY4BKm+Y03CdZNXyMDs/NTguDdfarawTIAIlJOSg5rV7S
74v73dtZkB5LySX1rBomRb5zkCg6/Ygap4RO1aO9gbXyPmc2lUQSBAJCS9d8h0qD
sNGembqhGDwHpQARAQABiQEfBBgBCgAJBQJYoqhaAhsMAAoJEJf6E/dgAHbdKl4H
/A/Q+hOh40M2B6Sh/Rsus+k1VL51+UXVJRyiNiqvofamufF5ULfeq3Pt5HfqpCsF
/A7iRP0MuanKcZbLDjDv6wWDuRvW9Kwn5qjbb6iBPh+wfyReqNMWGksJTyap0SNR
+Mn+sjbxGkUPjzVdRdqtw64R3DLW6SpMNIUVC6paazCViEtHsSauWwP/0+DRA/6j
xBHSAr0UaLsRTHleOSWV6ABrh9skJRWLBcOoi5pxotBs/3BmjqERQf9bwmmyxj2K
cE60Muv6qY4/KCQqPKhyFjJwXhS/e7BwxHEt1SbUb1aC4lH5UrbW9KYLu5lHJRdY
pfZ36xQbOAQYKKf6ZTT5R/Y=
=2pJ8
-----END PGP PUBLIC KEY BLOCK-----</string>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="8.5"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no"/>
</textView>
</subviews>
<constraints>
<constraint firstAttribute="bottomMargin" secondItem="23o-MP-wQY" secondAttribute="bottom" id="45J-dA-N4r"/>
<constraint firstItem="23o-MP-wQY" firstAttribute="top" secondItem="TQD-GC-YOY" secondAttribute="topMargin" id="Tth-YH-Ni9"/>
<constraint firstAttribute="trailingMargin" secondItem="23o-MP-wQY" secondAttribute="trailing" id="lqq-rV-fZK"/>
<constraint firstItem="23o-MP-wQY" firstAttribute="leading" secondItem="TQD-GC-YOY" secondAttribute="leadingMargin" constant="7" id="oYc-y1-XS9"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="WgM-cY-mig" id="7bV-Gr-WNu"/>
<outlet property="delegate" destination="WgM-cY-mig" id="lsz-m8-HZb"/>
</connections>
</tableView>
<connections>
<outlet property="armorPrivateKeyTextView" destination="23o-MP-wQY" id="ybK-Ba-vJt"/>
<outlet property="armorPublicKeyTextView" destination="Y9t-DD-6uH" id="307-xN-QZ9"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="qJO-AN-K9p" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="6870" y="3117"/>
</scene>
</scenes>
<resources>
<image name="Lock" width="25" height="25"/>

View file

@ -63,7 +63,7 @@ class AboutRepositoryTableViewController: BasicStaticTableViewController {
[[.style: CellDataStyle.value1, .accessoryType: type, .title: "Passwords", .detailText: numberOfPasswordsString],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Size", .detailText: sizeOfRepositoryString],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Local Commits", .detailText: String(self?.passwordStore.numberOfLocalCommits() ?? 0)],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Last Synced", .detailText: Utils.getLastUpdatedTimeString()],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Last Synced", .detailText: Utils.getLastSyncedTimeString()],
[.style: CellDataStyle.value1, .accessoryType: type, .title: "Commits", .detailText: numberOfCommitsString],
[.title: "Commit Logs", .action: "segue", .link: "showCommitLogsSegue"],
],

View file

@ -22,14 +22,15 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "saveEditPasswordSegue" {
let nameCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! ContentTableViewCell
if nameCell.getContent() != password?.name {
let alertTitle = "Cannot Save Edit"
let alertMessage = "Editing name is not supported."
Utils.alert(title: alertTitle, message: alertMessage, controller: self) {
nameCell.setContent(content: self.password!.name)
if let nameCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? ContentTableViewCell {
if nameCell.getContent() != password?.name {
let alertTitle = "Cannot Save Edit"
let alertMessage = "Editing name is not supported."
Utils.alert(title: alertTitle, message: alertMessage, controller: self) {
nameCell.setContent(content: self.password!.name)
}
return false
}
return false
}
}
return true

View file

@ -0,0 +1,72 @@
//
// GitSSHKeyArmorSettingTableViewController.swift
// pass
//
// Created by Mingshen Sun on 2/4/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SwiftyUserDefaults
class GitSSHKeyArmorSettingTableViewController: UITableViewController {
@IBOutlet weak var armorPublicKeyTextView: UITextView!
@IBOutlet weak var armorPrivateKeyTextView: UITextView!
var gitSSHPrivateKeyPassphrase: String?
let passwordStore = PasswordStore.shared
var doneBarButtonItem: UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
armorPublicKeyTextView.text = Defaults[.gitSSHPublicKeyArmor]
armorPrivateKeyTextView.text = Defaults[.gitSSHPrivateKeyArmor]
gitSSHPrivateKeyPassphrase = passwordStore.gitSSHPrivateKeyPassphrase
doneBarButtonItem = UIBarButtonItem(title: "Done",
style: UIBarButtonItemStyle.done,
target: self,
action: #selector(doneButtonTapped(_:)))
navigationItem.rightBarButtonItem = doneBarButtonItem
navigationItem.title = "SSH Key"
}
private func createSavePassphraseAlert() -> UIAlertController {
let savePassphraseAlert = UIAlertController(title: "Passphrase", message: "Do you want to save the passphrase for later sync?", preferredStyle: UIAlertControllerStyle.alert)
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in
Defaults[.isRememberPassphraseOn] = false
Defaults[.gitSSHKeySource] = "armor"
self.navigationController!.popViewController(animated: true)
})
savePassphraseAlert.addAction(UIAlertAction(title: "Save", style: UIAlertActionStyle.destructive) {_ in
Defaults[.isRememberPassphraseOn] = true
self.passwordStore.gitSSHPrivateKeyPassphrase = self.gitSSHPrivateKeyPassphrase
Defaults[.gitSSHKeySource] = "armor"
self.navigationController!.popViewController(animated: true)
})
return savePassphraseAlert
}
func doneButtonTapped(_ sender: Any) {
Defaults[.gitSSHPublicKeyArmor] = armorPublicKeyTextView.text
Defaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text
do {
try passwordStore.initGitSSHKey(with: armorPublicKeyTextView.text, .public)
try passwordStore.initGitSSHKey(with: armorPrivateKeyTextView.text, .secret)
} catch {
Utils.alert(title: "Cannot Save", message: "Cannot Save SSH Key", controller: self, completion: nil)
}
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your SSH secret key.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
self.gitSSHPrivateKeyPassphrase = alert.textFields?.first?.text
let savePassphraseAlert = self.createSavePassphraseAlert()
self.present(savePassphraseAlert, animated: true, completion: nil)
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = self.gitSSHPrivateKeyPassphrase
textField.isSecureTextEntry = true
})
self.present(alert, animated: true, completion: nil)
}
}

View file

@ -8,17 +8,18 @@
import UIKit
import SwiftyUserDefaults
import SVProgressHUD
class GitServerSettingTableViewController: UITableViewController {
@IBOutlet weak var gitRepositoryURLTextField: UITextField!
@IBOutlet weak var gitURLTextField: UITextField!
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var authSSHKeyCell: UITableViewCell!
@IBOutlet weak var authPasswordCell: UITableViewCell!
let passwordStore = PasswordStore.shared
var password: String?
var authenticationMethod = Defaults[.gitRepositoryAuthenticationMethod]
var authenticationMethod = Defaults[.gitAuthenticationMethod]
private func checkAuthenticationMethod(method: String) {
let passwordCheckView = authPasswordCell.viewWithTag(1001)!
@ -38,17 +39,17 @@ class GitServerSettingTableViewController: UITableViewController {
}
override func viewDidLoad() {
super.viewDidLoad()
if let url = Defaults[.gitRepositoryURL] {
gitRepositoryURLTextField.text = url.absoluteString
if let url = Defaults[.gitURL] {
gitURLTextField.text = url.absoluteString
}
usernameTextField.text = Defaults[.gitRepositoryUsername]
password = passwordStore.gitRepositoryPassword
authenticationMethod = Defaults[.gitRepositoryAuthenticationMethod]
usernameTextField.text = Defaults[.gitUsername]
password = passwordStore.gitPassword
authenticationMethod = Defaults[.gitAuthenticationMethod]
// Grey out ssh option if ssh_key and ssh_key.pub are not present
let sshLabel = authSSHKeyCell.subviews[0].subviews[0] as! UILabel
sshLabel.isEnabled = sshKeyExists()
sshLabel.isEnabled = gitSSHKeyExists()
if authenticationMethod == nil || !sshLabel.isEnabled {
authenticationMethod = "Password"
@ -61,7 +62,7 @@ class GitServerSettingTableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
if cell == authSSHKeyCell {
performSegue(withIdentifier: "showSSHKeySettingSegue", sender: self)
showSSHKeyActionSheet()
}
}
@ -77,7 +78,7 @@ class GitServerSettingTableViewController: UITableViewController {
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "saveGitServerSettingSegue" {
guard let _ = URL(string: gitRepositoryURLTextField.text!) else {
guard let _ = URL(string: gitURLTextField.text!) else {
Utils.alert(title: "Cannot Save", message: "Git Server is not set.", controller: self, completion: nil)
return false
}
@ -89,18 +90,13 @@ class GitServerSettingTableViewController: UITableViewController {
return true
}
func sshKeyExists() -> Bool {
return FileManager.default.fileExists(atPath: Globals.sshPublicKeyURL.path) &&
FileManager.default.fileExists(atPath: Globals.sshPrivateKeyURL.path)
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
if cell == authPasswordCell {
authenticationMethod = "Password"
} else if cell == authSSHKeyCell {
if !sshKeyExists() {
if !gitSSHKeyExists() {
Utils.alert(title: "Cannot Select SSH Key", message: "Please setup SSH key first.", controller: self, completion: nil)
authenticationMethod = "Password"
} else {
@ -131,4 +127,74 @@ class GitServerSettingTableViewController: UITableViewController {
}
}
}
private func gitSSHKeyExists() -> Bool {
return FileManager.default.fileExists(atPath: Globals.gitSSHPublicKeyPath) &&
FileManager.default.fileExists(atPath: Globals.gitSSHPrivateKeyPath)
}
func showSSHKeyActionSheet() {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var urlActionTitle = "Download from URL"
var armorActionTitle = "ASCII-Armor Encrypted Key"
var fileActionTitle = "Use Uploaded Keys"
if Defaults[.gitSSHKeySource] == "url" {
urlActionTitle = "\(urlActionTitle)"
} else if Defaults[.gitSSHKeySource] == "armor" {
armorActionTitle = "\(armorActionTitle)"
} else if Defaults[.gitSSHKeySource] == "file" {
fileActionTitle = "\(fileActionTitle)"
}
let urlAction = UIAlertAction(title: urlActionTitle, style: .default) { _ in
self.performSegue(withIdentifier: "setGitSSHKeyByURLSegue", sender: self)
}
let armorAction = UIAlertAction(title: armorActionTitle, style: .default) { _ in
self.performSegue(withIdentifier: "setGitSSHKeyByArmorSegue", sender: self)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
optionMenu.addAction(urlAction)
optionMenu.addAction(armorAction)
if (gitSSHKeyExists()) {
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
let alert = UIAlertController(
title: "SSH Key Passphrase",
message: "Please fill in the passphrase for your Git Repository SSH key.",
preferredStyle: UIAlertControllerStyle.alert
)
alert.addAction(
UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: {_ in
self.passwordStore.gitSSHPrivateKeyPassphrase = alert.textFields!.first!.text!
}
)
)
alert.addTextField(
configurationHandler: {(textField: UITextField!) in
textField.text = self.passwordStore.gitSSHPrivateKeyPassphrase
textField.isSecureTextEntry = true
}
)
}
Defaults[.gitSSHKeySource] = "file"
optionMenu.addAction(fileAction)
}
if Defaults[.gitSSHKeySource] != nil {
let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in
Utils.removeGitSSHKeys()
Defaults[.gitSSHKeySource] = nil
}
optionMenu.addAction(deleteAction)
}
optionMenu.addAction(cancelAction)
optionMenu.popoverPresentationController?.sourceView = authSSHKeyCell
optionMenu.popoverPresentationController?.sourceRect = authSSHKeyCell.bounds
self.present(optionMenu, animated: true, completion: nil)
}
}

View file

@ -39,6 +39,11 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
content = ""
}
init(title: String) {
self.title = title
self.content = ""
}
init(title: String, content: String) {
self.title = title
self.content = content
@ -46,24 +51,39 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
private struct TableSection {
var title: String
var type: PasswordDetailTableViewControllerSectionType
var header: String?
var item: Array<TableCell>
init(type: PasswordDetailTableViewControllerSectionType) {
self.type = type
header = nil
item = [TableCell]()
}
init(type: PasswordDetailTableViewControllerSectionType, header: String) {
self.init(type: type)
self.header = header
}
}
private var tableData = Array<TableSection>()
private enum PasswordDetailTableViewControllerSectionType {
case name, main, addition, misc
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell")
passwordCategoryText = passwordEntity!.getCategoryText()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:)))
tapGesture.cancelsTouchesInView = false
tableView.addGestureRecognizer(tapGesture)
tapGesture.delegate = self
tableView.contentInset = UIEdgeInsetsMake(-36, 0, 0, 0);
tableView.contentInset = UIEdgeInsetsMake(-36, 0, 44, 0);
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 52
@ -224,23 +244,31 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
private func setTableData() {
self.tableData = Array<TableSection>()
tableData.append(TableSection(title: "", item: []))
tableData[0].item.append(TableCell())
var tableDataIndex = 1
self.tableData.append(TableSection(title: "", item: []))
// name section
var section = TableSection(type: .name)
section.item.append(TableCell())
tableData.append(section)
// main section
section = TableSection(type: .main)
let password = self.password!
if let username = password.getUsername() {
self.tableData[tableDataIndex].item.append(TableCell(title: "username", content: username))
section.item.append(TableCell(title: "username", content: username))
}
self.tableData[tableDataIndex].item.append(TableCell(title: "password", content: password.password))
section.item.append(TableCell(title: "password", content: password.password))
tableData.append(section)
// addition section
// show one time password
if password.otpType != .none {
if let (title, otp) = self.password?.getOtpStrings() {
self.tableData.append(TableSection(title: "One time password", item: []))
tableDataIndex += 1
oneTimePasswordIndexPath = IndexPath(row: 0, section: tableDataIndex)
self.tableData[tableDataIndex].item.append(TableCell(title: title, content: otp))
section = TableSection(type: .addition, header: "One Time Password")
section.item.append(TableCell(title: title, content: otp))
tableData.append(section)
oneTimePasswordIndexPath = IndexPath(row: 0, section: tableData.count - 1)
}
}
@ -252,12 +280,18 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
(!Password.otpKeywords.contains($0) || !Defaults[.isHideOTPOn]) }
if filteredAdditionKeys.count > 0 {
self.tableData.append(TableSection(title: "additions", item: []))
tableDataIndex += 1
section = TableSection(type: .addition, header: "additions")
for additionKey in filteredAdditionKeys {
self.tableData[tableDataIndex].item.append(TableCell(title: additionKey, content: password.additions[additionKey]!))
section.item.append(TableCell(title: additionKey, content: password.additions[additionKey]!))
}
tableData.append(section)
}
// misc section
section = TableSection(type: .misc)
section.item.append(TableCell(title: "Show Raw"))
tableData.append(section)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
@ -267,6 +301,12 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
editController.password = password
}
}
} else if segue.identifier == "showRawPasswordSegue" {
if let controller = segue.destination as? UINavigationController {
if let controller = controller.viewControllers.first as? RawPasswordViewController {
controller.password = password
}
}
}
}
@ -328,6 +368,16 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view!.isKind(of: UIButton.classForCoder()) {
return false
}
return true
}
@IBAction func back(segue:UIStoryboardSegue) {
}
func getNextHOTP() {
guard password != nil, passwordEntity != nil, password?.otpType == .hotp else {
DispatchQueue.main.async {
@ -380,8 +430,9 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sectionIndex = indexPath.section
let rowIndex = indexPath.row
if sectionIndex == 0 && rowIndex == 0 {
let tableDataItem = tableData[sectionIndex].item[rowIndex]
switch(tableData[sectionIndex].type) {
case .name:
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordDetailTitleTableViewCell", for: indexPath) as! PasswordDetailTitleTableViewCell
cell.passwordImageImageView.image = passwordImage ?? #imageLiteral(resourceName: "PasswordImagePlaceHolder")
var passwordName = passwordEntity!.name!
@ -390,22 +441,29 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
cell.nameLabel.text = passwordName
cell.categoryLabel.text = passwordCategoryText
cell.selectionStyle = .none
return cell
} else {
case .main, .addition:
let cell = tableView.dequeueReusableCell(withIdentifier: "labelCell", for: indexPath) as! LabelTableViewCell
let titleData = tableData[sectionIndex].item[rowIndex].title
let contentData = tableData[sectionIndex].item[rowIndex].content
let titleData = tableDataItem.title
let contentData = tableDataItem.content
cell.delegatePasswordTableView = self
cell.isPasswordCell = (titleData.lowercased() == "password" ? true : false)
cell.isURLCell = (titleData.lowercased() == "url" ? true : false)
cell.isHOTPCell = (titleData == "HMAC-based" ? true : false)
cell.cellData = LabelTableViewCellData(title: titleData, content: contentData)
cell.selectionStyle = .none
return cell
case .misc:
let cell = UITableViewCell()
cell.textLabel?.text = tableDataItem.title
cell.selectionStyle = .default
return cell
}
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return tableData[section].title
return tableData[section].header
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
@ -430,10 +488,26 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
override func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
return action == #selector(UIResponderStandardEditActions.copy(_:))
let section = tableData[indexPath.section]
switch(section.type) {
case .main, .addition:
return action == #selector(UIResponderStandardEditActions.copy(_:))
default:
return false
}
}
override func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let section = tableData[indexPath.section]
if section.type == .misc {
if section.item[indexPath.row].title == "Show Raw" {
performSegue(withIdentifier: "showRawPasswordSegue", sender: self)
}
}
tableView.deselectRow(at: indexPath, animated: true)
}
}

View file

@ -7,6 +7,8 @@
//
import UIKit
import SwiftyUserDefaults
enum PasswordEditorCellType {
case textFieldCell, textViewCell, fillPasswordCell, passwordLengthCell, deletePasswordCell
}
@ -54,11 +56,6 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
tableView.estimatedRowHeight = 48
self.tableView.sectionFooterHeight = UITableViewAutomaticDimension;
self.tableView.estimatedSectionFooterHeight = 0;
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tableTapped))
tapGesture.delegate = self
tapGesture.cancelsTouchesInView = false
tableView.addGestureRecognizer(tapGesture)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@ -76,7 +73,12 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
return fillPasswordCell!
case .passwordLengthCell:
passwordLengthCell = tableView.dequeueReusableCell(withIdentifier: "passwordLengthCell", for: indexPath) as? SliderTableViewCell
passwordLengthCell?.reset(title: "Length", minimumValue: Globals.passwordMinimumLength, maximumValue: Globals.passwordMaximumLength, defaultValue: Globals.passwordDefaultLength)
let lengthSetting = Globals.passwordDefaultLength[Defaults[.passwordGeneratorFlavor]] ??
Globals.passwordDefaultLength["Random"]
passwordLengthCell?.reset(title: "Length",
minimumValue: lengthSetting?.min ?? 0,
maximumValue: lengthSetting?.max ?? 0,
defaultValue: lengthSetting?.def ?? 0)
passwordLengthCell?.delegate = self
return passwordLengthCell!
case .deletePasswordCell:
@ -148,7 +150,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
hidePasswordSettings = false
tableView.reloadSections([passwordSection], with: .fade)
}
let length = passwordLengthCell?.roundedValue ?? Globals.passwordDefaultLength
let length = passwordLengthCell?.roundedValue ?? 0
let plainPassword = Utils.generatePassword(length: length)
Utils.copyToPasteboard(textToCopy: plainPassword)
@ -159,35 +161,8 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
fillPasswordCell?.setContent(content: plainPassword)
}
func tableTapped(recognizer: UITapGestureRecognizer) {
if recognizer.state == UIGestureRecognizerState.ended {
let tapLocation = recognizer.location(in: self.tableView)
let tapIndexPath = self.tableView.indexPathForRow(at: tapLocation)
// do nothing, if delete is tapped (a temporary solution)
if tapIndexPath != nil, deletePasswordCell != nil,
tableView.cellForRow(at: tapIndexPath!) == deletePasswordCell {
return
}
// hide password settings (e.g., the length slider)
if tapIndexPath?.section != passwordSection, hidePasswordSettings == false {
hidePasswordSettings = true
tableView.reloadSections([passwordSection], with: .fade)
// select the row at tapIndexPath manually
if tapIndexPath != nil {
self.tableView(self.tableView, didSelectRowAt: tapIndexPath!)
}
}
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UITapGestureRecognizer {
// so that the tap gesture could be passed by
return true
} else {
return false
}
func showHidePasswordSettings() {
hidePasswordSettings = !hidePasswordSettings
tableView.reloadSections([passwordSection], with: .fade)
}
}

View file

@ -86,7 +86,6 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(parent: parent)
} else {
passwordEntities = self.passwordStore.fetchPasswordEntityCoreData(withDir: false)
}
passwordsTableEntries = passwordEntities.map {
PasswordsTableEntry(title: $0.name!, isDir: $0.isDir, passwordEntity: $0)
@ -109,8 +108,8 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
SVProgressHUD.showProgress(progress, status: "Encrypting")
}
})
DispatchQueue.main.async {
// will trigger reloadTableView() by a notification
SVProgressHUD.showSuccess(withStatus: "Done")
SVProgressHUD.dismiss(withDelay: 1)
}
@ -144,8 +143,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
DispatchQueue.main.async {
self.reloadTableView(parent: nil)
Defaults[.lastUpdatedTime] = Date()
Defaults[.gitRepositoryPasswordAttempts] = 0
Defaults[.gitPasswordAttempts] = 0
SVProgressHUD.showSuccess(withStatus: "Done")
SVProgressHUD.dismiss(withDelay: 1)
}
@ -174,9 +172,9 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
reloadTableView(parent: nil)
// reset the data table if some password (maybe another one) has been updated
NotificationCenter.default.addObserver(self, selector: #selector(reloadTableView as (Void) -> Void), name: .passwordStoreUpdated, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordStoreUpdated, object: nil)
// reset the data table if the disaply settings have been changed
NotificationCenter.default.addObserver(self, selector: #selector(reloadTableView as (Void) -> Void), name: .passwordDisplaySettingChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordDisplaySettingChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(actOnSearchNotification), name: .passwordSearch, object: nil)
}
@ -221,20 +219,23 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} else {
cell.textLabel?.text = "\(entry.title)"
cell.accessoryType = .disclosureIndicator
cell.detailTextLabel?.font = UIFont.preferredFont(forTextStyle: .body)
cell.detailTextLabel?.text = "\(entry.passwordEntity?.children?.count ?? 0)"
}
return cell
} else {
let passwordWithFolderCell = tableView.dequeueReusableCell(withIdentifier: "passwordWithFolderTableViewCell", for: indexPath) as! PasswordWithFolderTableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
let entry = getPasswordEntry(by: indexPath)
if entry.passwordEntity!.synced {
passwordWithFolderCell.passwordLabel?.text = entry.title
cell.textLabel?.text = entry.title
} else {
passwordWithFolderCell.passwordLabel?.text = "\(entry.title)"
cell.textLabel?.text = "\(entry.title)"
}
passwordWithFolderCell.folderLabel.text = entry.passwordEntity?.getCategoryText()
passwordWithFolderCell.addGestureRecognizer(longPressGestureRecognizer)
return passwordWithFolderCell
cell.accessoryType = .none
cell.detailTextLabel?.font = UIFont.preferredFont(forTextStyle: .footnote)
cell.detailTextLabel?.text = entry.passwordEntity?.getCategoryText()
cell.addGestureRecognizer(longPressGestureRecognizer)
return cell
}
}
@ -429,7 +430,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
self.tableView.layer.removeAnimation(forKey: "UITableViewReloadDataAnimationKey")
// set the sync control title
let atribbutedTitle = "Last Synced: \(Utils.getLastUpdatedTimeString())"
let atribbutedTitle = "Last Synced: \(Utils.getLastSyncedTimeString())"
syncControl.attributedTitle = NSAttributedString(string: atribbutedTitle)
}
@ -438,9 +439,12 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
reloadTableView(data: passwordsTableEntries, anim: anim)
}
func reloadTableView() {
func actOnReloadTableViewRelatedNotification() {
initPasswordsTableEntries(parent: nil)
reloadTableView(data: passwordsTableEntries)
DispatchQueue.main.async { [weak weakSelf = self] in
guard let strongSelf = weakSelf else { return }
strongSelf.reloadTableView(data: strongSelf.passwordsTableEntries)
}
}
func handleRefresh(_ syncControl: UIRefreshControl) {

View file

@ -0,0 +1,21 @@
//
// RawPasswordViewController.swift
// pass
//
// Created by Mingshen Sun on 31/3/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
class RawPasswordViewController: UIViewController {
@IBOutlet weak var rawPasswordTextView: UITextView!
var password: Password?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Raw"
rawPasswordTextView.text = password?.plainText
}
}

View file

@ -12,15 +12,14 @@ import SVProgressHUD
class SSHKeySettingTableViewController: UITableViewController {
@IBOutlet weak var passphraseTextField: UITextField!
@IBOutlet weak var privateKeyURLTextField: UITextField!
@IBOutlet weak var publicKeyURLTextField: UITextField!
let passwordStore = PasswordStore.shared
override func viewDidLoad() {
super.viewDidLoad()
passphraseTextField.text = Utils.getPasswordFromKeychain(name: "gitRepositorySSHPrivateKeyPassphrase") ?? ""
privateKeyURLTextField.text = Defaults[.gitRepositorySSHPrivateKeyURL]?.absoluteString
publicKeyURLTextField.text = Defaults[.gitRepositorySSHPublicKeyURL]?.absoluteString
privateKeyURLTextField.text = Defaults[.gitSSHPrivateKeyURL]?.absoluteString
publicKeyURLTextField.text = Defaults[.gitSSHPublicKeyURL]?.absoluteString
var doneBarButtonItem: UIBarButtonItem?
doneBarButtonItem = UIBarButtonItem(title: "Done",
@ -41,18 +40,42 @@ class SSHKeySettingTableViewController: UITableViewController {
return
}
Defaults[.gitRepositorySSHPublicKeyURL] = publicKeyURL
Defaults[.gitRepositorySSHPrivateKeyURL] = privateKeyURL
Utils.addPasswordToKeychain(name: "gitRepositorySSHPrivateKeyPassphrase", password: passphraseTextField.text!)
Defaults[.gitSSHPublicKeyURL] = publicKeyURL
Defaults[.gitSSHPrivateKeyURL] = privateKeyURL
do {
try Data(contentsOf: publicKeyURL).write(to: Globals.sshPublicKeyURL, options: .atomic)
try Data(contentsOf: privateKeyURL).write(to: Globals.sshPrivateKeyURL, options: .atomic)
try Data(contentsOf: publicKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPublicKeyPath), options: .atomic)
try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPrivateKeyPath), options: .atomic)
} catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
}
navigationController!.popViewController(animated: true)
Defaults[.gitSSHKeySource] = "url"
let alert = UIAlertController(
title: "PGP Passphrase",
message: "Please fill in the passphrase for your Git Repository SSH key.",
preferredStyle: UIAlertControllerStyle.alert
)
alert.addAction(
UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: {_ in
Utils.addPasswordToKeychain(
name: "gitSSHPrivateKeyPassphrase",
password: alert.textFields!.first!.text!
)
self.navigationController!.popViewController(animated: true)
}
)
)
alert.addTextField(
configurationHandler: {(textField: UITextField!) in
textField.text = self.passwordStore.gitSSHPrivateKeyPassphrase
textField.isSecureTextEntry = true
})
self.present(alert, animated: true, completion: nil)
}
}

View file

@ -95,16 +95,16 @@ class SettingsTableViewController: UITableViewController {
@IBAction func saveGitServerSetting(segue: UIStoryboardSegue) {
if let controller = segue.source as? GitServerSettingTableViewController {
let gitRepostiroyURL = controller.gitRepositoryURLTextField.text!
let gitRepostiroyURL = controller.gitURLTextField.text!
let username = controller.usernameTextField.text!
let password = controller.password
let auth = controller.authenticationMethod
if Defaults[.gitRepositoryURL] == nil ||
Defaults[.gitRepositoryURL]!.absoluteString != gitRepostiroyURL ||
auth != Defaults[.gitRepositoryAuthenticationMethod] ||
username != Defaults[.gitRepositoryUsername] ||
password != self.passwordStore.gitRepositoryPassword ||
if Defaults[.gitURL] == nil ||
Defaults[.gitURL]!.absoluteString != gitRepostiroyURL ||
auth != Defaults[.gitAuthenticationMethod] ||
username != Defaults[.gitUsername] ||
password != self.passwordStore.gitPassword ||
self.passwordStore.repositoryExisted() == false {
SVProgressHUD.setDefaultMaskType(.black)
@ -117,9 +117,9 @@ class SettingsTableViewController: UITableViewController {
gitCredential = GitCredential(
credential: GitCredential.Credential.ssh(
userName: username,
password: Utils.getPasswordFromKeychain(name: "gitRepositorySSHPrivateKeyPassphrase") ?? "",
publicKeyFile: Globals.sshPublicKeyURL,
privateKeyFile: Globals.sshPrivateKeyURL,
password: Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase") ?? "",
publicKeyFile: Globals.gitSSHPublicKeyURL,
privateKeyFile: Globals.gitSSHPrivateKeyURL,
passwordNotSetCallback: self.requestSshKeyPassword
)
)
@ -140,12 +140,11 @@ class SettingsTableViewController: UITableViewController {
}
})
DispatchQueue.main.async {
Defaults[.lastUpdatedTime] = Date()
Defaults[.gitRepositoryURL] = URL(string: gitRepostiroyURL)
Defaults[.gitRepositoryUsername] = username
Defaults[.gitRepositoryAuthenticationMethod] = auth
Defaults[.gitRepositoryPasswordAttempts] = 0
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitRepositoryURL]?.host
Defaults[.gitURL] = URL(string: gitRepostiroyURL)
Defaults[.gitUsername] = username
Defaults[.gitAuthenticationMethod] = auth
Defaults[.gitPasswordAttempts] = 0
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]?.host
SVProgressHUD.showSuccess(withStatus: "Done")
SVProgressHUD.dismiss(withDelay: 1)
}
@ -163,7 +162,7 @@ class SettingsTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: .passwordStoreErased, object: nil)
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitRepositoryURL]?.host
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]?.host
touchIDTableViewCell.accessoryView = touchIDSwitch
setPGPKeyTableViewCellDetailText()
setPasswordRepositoryTableViewCellDetailText()
@ -190,10 +189,10 @@ class SettingsTableViewController: UITableViewController {
}
private func setPasswordRepositoryTableViewCellDetailText() {
if Defaults[.gitRepositoryURL] == nil {
if Defaults[.gitURL] == nil {
passwordRepositoryTableViewCell.detailTextLabel?.text = "Not Set"
} else {
passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitRepositoryURL]!.host
passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]!.host
}
}
@ -219,7 +218,7 @@ class SettingsTableViewController: UITableViewController {
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = self.passwordStore.gitRepositoryPassword
textField.text = self.passwordStore.gitPassword
textField.isSecureTextEntry = true
})

View file

@ -17,14 +17,19 @@ extension DefaultsKeys {
static let pgpPublicKeyArmor = DefaultsKey<String?>("pgpPublicKeyArmor")
static let pgpPrivateKeyArmor = DefaultsKey<String?>("pgpPrivateKeyArmor")
static let gitRepositoryURL = DefaultsKey<URL?>("gitRepositoryURL")
static let gitRepositoryAuthenticationMethod = DefaultsKey<String?>("gitRepositoryAuthenticationMethod")
static let gitRepositoryUsername = DefaultsKey<String?>("gitRepositoryUsername")
static let gitRepositoryPasswordAttempts = DefaultsKey<Int>("gitRepositoryPasswordAttempts")
static let gitRepositorySSHPublicKeyURL = DefaultsKey<URL?>("gitRepositorySSHPublicKeyURL")
static let gitRepositorySSHPrivateKeyURL = DefaultsKey<URL?>("gitRepositorySSHPrivateKeyURL")
static let gitURL = DefaultsKey<URL?>("gitURL")
static let gitAuthenticationMethod = DefaultsKey<String?>("gitAuthenticationMethod")
static let gitUsername = DefaultsKey<String?>("gitUsername")
static let gitPasswordAttempts = DefaultsKey<Int>("gitPasswordAttempts")
static let gitSSHPublicKeyURL = DefaultsKey<URL?>("gitSSHPublicKeyURL")
static let gitSSHPrivateKeyURL = DefaultsKey<URL?>("gitSSHPrivateKeyURL")
static let gitSSHKeySource = DefaultsKey<String?>("gitSSHKeySource")
static let gitSSHPublicKeyArmor = DefaultsKey<String?>("gitSSHPublicKeyArmor")
static let gitSSHPrivateKeyArmor = DefaultsKey<String?>("gitSSHPrivateKeyArmor")
static let lastUpdatedTime = DefaultsKey<Date?>("lasteUpdatedTime")
static let lastSyncedTime = DefaultsKey<Date?>("lastSyncedTime")
static let isTouchIDOn = DefaultsKey<Bool>("isTouchIDOn")
static let passcodeKey = DefaultsKey<String?>("passcodeKey")

View file

@ -15,22 +15,25 @@ class Globals {
static let pgpPublicKeyPath = "\(documentPath)/gpg_key.pub"
static let pgpPrivateKeyPath = "\(documentPath)/gpg_key"
static let sshPublicKeyURL = URL(fileURLWithPath: "\(documentPath)/ssh_key.pub")
static let sshPrivateKeyURL = URL(fileURLWithPath: "\(documentPath)/ssh_key")
static let gitSSHPublicKeyPath = "\(documentPath)/ssh_key.pub"
static let gitSSHPrivateKeyPath = "\(documentPath)/ssh_key"
static let gitSSHPublicKeyURL = URL(fileURLWithPath: gitSSHPublicKeyPath)
static let gitSSHPrivateKeyURL = URL(fileURLWithPath: gitSSHPrivateKeyPath)
static let repositoryPath = "\(libraryPath)/password-store"
static var passcodeConfiguration = PasscodeLockConfiguration()
static let red = UIColor(red:1.00, green:0.23, blue:0.19, alpha:1.0)
static let blue = UIColor(red:0.00, green:0.48, blue:1.00, alpha:1.0)
static let passwordMinimumLength = 6
static let passwordMaximumLength = 24
static let passwordDefaultLength = 16
static let passwordDefaultLength = ["Random": (min: 6, max: 24, def: 16),
"Apple": (min: 15, max: 15, def: 15)]
static let passwordDots = "••••••••••••"
static let passwordFonts = "Menlo"
// UI related
static let red = UIColor(red:1.00, green:0.23, blue:0.19, alpha:1.0)
static let blue = UIColor(red:0.00, green:0.48, blue:1.00, alpha:1.0)
static let tableCellButtonSize = CGFloat(20.0)
private init() { }
}

View file

@ -27,15 +27,14 @@ class Utils {
removeFileIfExists(atPath: url.path)
}
static func getLastUpdatedTimeString() -> String {
var lastUpdatedTimeString = ""
if let lastUpdatedTime = Defaults[.lastUpdatedTime] {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
lastUpdatedTimeString = formatter.string(from: lastUpdatedTime)
static func getLastSyncedTimeString() -> String {
guard let lastSyncedTime = Defaults[.lastSyncedTime] else {
return "Oops! Sync again?"
}
return lastUpdatedTimeString
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
return formatter.string(from: lastSyncedTime)
}
static func generatePassword(length: Int) -> String{
@ -84,6 +83,16 @@ class Utils {
Utils.removeKeychain(name: ".pgpKeyPassphrase")
}
static func removeGitSSHKeys() {
removeFileIfExists(atPath: Globals.gitSSHPublicKeyPath)
removeFileIfExists(atPath: Globals.gitSSHPrivateKeyPath)
Defaults.remove(.gitSSHPublicKeyArmor)
Defaults.remove(.gitSSHPrivateKeyArmor)
Defaults.remove(.gitSSHPublicKeyURL)
Defaults.remove(.gitSSHPrivateKeyURL)
Utils.removeKeychain(name: ".gitSSHPrivateKeyPassphrase")
}
static func getPasswordFromKeychain(name: String) -> String? {
let keychain = Keychain(service: "me.mssun.passforios")
do {

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.2.2</string>
<string>0.2.3</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>ITSAppUsesNonExemptEncryption</key>

View file

@ -24,8 +24,8 @@ class Password {
var additions = [String: String]()
var additionKeys = [String]()
var changed = false
var plainText = ""
private var plainText = ""
private var firstLineIsOTPField = false
private var otpToken: Token?
@ -99,14 +99,17 @@ class Password {
// return a key-value pair from the line
// key might be nil, if there is no ":" in the line
static private func getKeyValuePair(from line: String) -> (key: String?, value: String) {
let items = line.characters.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: true).map(String.init)
var key : String?
let items = line.characters.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: false).map{String($0).trimmingCharacters(in: .whitespaces)}
var key : String? = nil
var value = ""
if items.count == 1 {
value = items[0]
} else if items.count == 2 {
key = items[0]
value = items[1].trimmingCharacters(in: .whitespaces)
if items.count == 1 || (items[0].isEmpty && items[1].isEmpty) {
// no ":" found, or empty on both sides of ":" (e.g., " : ")
value = line
} else {
if !items[0].isEmpty {
key = items[0]
}
value = items[1]
}
return (key, value)
}
@ -178,6 +181,8 @@ class Password {
*/
private func updateOtpToken() {
self.otpToken = nil
// get otpauth, if we are able to generate a token, return
if var otpauthString = getAdditionValue(withKey: "otpauth") {
if !otpauthString.hasPrefix("otpauth:") {

View file

@ -27,9 +27,9 @@ struct GitCredential {
var credential: GTCredential? = nil
switch self.credential {
case let .http(userName, password):
print(Defaults[.gitRepositoryPasswordAttempts])
print(Defaults[.gitPasswordAttempts])
var newPassword: String = password
if Defaults[.gitRepositoryPasswordAttempts] != 0 {
if Defaults[.gitPasswordAttempts] != 0 {
let sem = DispatchSemaphore(value: 0)
DispatchQueue.main.async {
SVProgressHUD.dismiss()
@ -40,15 +40,15 @@ struct GitCredential {
let alert = UIAlertController(title: "Password", message: "Please fill in the password of your Git account.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
newPassword = alert.textFields!.first!.text!
PasswordStore.shared.gitRepositoryPassword = newPassword
PasswordStore.shared.gitPassword = newPassword
sem.signal()
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
Defaults[.gitRepositoryPasswordAttempts] = -1
Defaults[.gitPasswordAttempts] = -1
sem.signal()
})
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = PasswordStore.shared.gitRepositoryPassword
textField.text = PasswordStore.shared.gitPassword
textField.isSecureTextEntry = true
})
topController.present(alert, animated: true, completion: nil)
@ -56,12 +56,12 @@ struct GitCredential {
}
let _ = sem.wait(timeout: DispatchTime.distantFuture)
}
if Defaults[.gitRepositoryPasswordAttempts] == -1 {
Defaults[.gitRepositoryPasswordAttempts] = 0
if Defaults[.gitPasswordAttempts] == -1 {
Defaults[.gitPasswordAttempts] = 0
return nil
}
Defaults[.gitRepositoryPasswordAttempts] += 1
PasswordStore.shared.gitRepositoryPassword = newPassword
Defaults[.gitPasswordAttempts] += 1
PasswordStore.shared.gitPassword = newPassword
credential = try? GTCredential(userName: userName, password: newPassword)
case let .ssh(userName, password, publicKeyFile, privateKeyFile, passwordNotSetCallback):
@ -76,7 +76,7 @@ struct GitCredential {
}
// Save password for the future
Utils.addPasswordToKeychain(name: "gitRepositorySSHPrivateKeyPassphrase", password: newPassword!)
Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newPassword!)
// nil is expected in case of empty password
if newPassword == "" {
@ -112,7 +112,7 @@ class PasswordStore {
var gitSignatureForNow: GTSignature {
get {
return GTSignature(name: Defaults[.gitRepositoryUsername]!, email: Defaults[.gitRepositoryUsername]!+"@passforios", time: Date())!
return GTSignature(name: Defaults[.gitUsername]!, email: Defaults[.gitUsername]!+"@passforios", time: Date())!
}
}
@ -126,12 +126,21 @@ class PasswordStore {
return Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase")
}
}
var gitRepositoryPassword: String? {
var gitPassword: String? {
set {
Utils.addPasswordToKeychain(name: "gitRepositoryPassword", password: newValue)
Utils.addPasswordToKeychain(name: "gitPassword", password: newValue)
}
get {
return Utils.getPasswordFromKeychain(name: "gitRepositoryPassword")
return Utils.getPasswordFromKeychain(name: "gitPassword")
}
}
var gitSSHPrivateKeyPassphrase: String? {
set {
Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newValue)
}
get {
return Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase") ?? ""
}
}
@ -164,22 +173,41 @@ class PasswordStore {
print(error)
}
initPGPKeys()
if Defaults[.gitRepositoryAuthenticationMethod] == "Password" {
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitRepositoryUsername]!, password: Utils.getPasswordFromKeychain(name: "gitRepositoryPassword") ?? ""))
} else if Defaults[.gitRepositoryAuthenticationMethod] == "SSH Key"{
initGitCredential()
}
enum SSHKeyType {
case `public`, secret
}
public func initGitCredential() {
if Defaults[.gitAuthenticationMethod] == "Password" {
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitUsername]!, password: Utils.getPasswordFromKeychain(name: "gitPassword") ?? ""))
} else if Defaults[.gitAuthenticationMethod] == "SSH Key"{
gitCredential = GitCredential(
credential: GitCredential.Credential.ssh(
userName: Defaults[.gitRepositoryUsername]!,
password: Utils.getPasswordFromKeychain(name: "gitRepositorySSHPrivateKeyPassphrase") ?? "",
publicKeyFile: Globals.sshPublicKeyURL,
privateKeyFile: Globals.sshPrivateKeyURL,
userName: Defaults[.gitUsername]!,
password: gitSSHPrivateKeyPassphrase ?? "",
publicKeyFile: Globals.gitSSHPublicKeyURL,
privateKeyFile: Globals.gitSSHPrivateKeyURL,
passwordNotSetCallback: nil
)
)
} else {
gitCredential = nil
}
}
public func initGitSSHKey(with armorKey: String, _ keyType: SSHKeyType) throws {
var keyPath = ""
switch keyType {
case .public:
keyPath = Globals.gitSSHPublicKeyPath
case .secret:
keyPath = Globals.gitSSHPrivateKeyPath
}
try armorKey.write(toFile: keyPath, atomically: true, encoding: .ascii)
}
public func initPGPKeys() {
@ -302,9 +330,11 @@ class PasswordStore {
}
storeRepository = try GTRepository(url: storeURL)
gitCredential = credential
self.updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
Defaults[.lastSyncedTime] = Date()
DispatchQueue.main.async {
self.updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
}
}
func pullRepository(transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void) throws {
@ -317,9 +347,12 @@ class PasswordStore {
]
let remote = try GTRemote(name: "origin", in: storeRepository!)
try storeRepository?.pull((storeRepository?.currentBranch())!, from: remote, withOptions: options, progress: transferProgressBlock)
self.setAllSynced()
self.updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
Defaults[.lastSyncedTime] = Date()
DispatchQueue.main.async {
self.setAllSynced()
self.updatePasswordEntityCoreData()
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
}
}
private func updatePasswordEntityCoreData() {
@ -649,8 +682,8 @@ class PasswordStore {
Utils.removeFileIfExists(atPath: Globals.pgpPublicKeyPath)
Utils.removeFileIfExists(atPath: Globals.pgpPrivateKeyPath)
Utils.removeFileIfExists(at: Globals.sshPrivateKeyURL)
Utils.removeFileIfExists(at: Globals.sshPublicKeyURL)
Utils.removeFileIfExists(atPath: Globals.gitSSHPublicKeyPath)
Utils.removeFileIfExists(atPath: Globals.gitSSHPrivateKeyPath)
Utils.removeAllKeychain()
@ -678,6 +711,8 @@ class PasswordStore {
try self.storeRepository?.reset(to: newHead, resetType: GTRepositoryResetType.hard)
self.setAllSynced()
self.updatePasswordEntityCoreData()
Defaults[.lastSyncedTime] = nil
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
NotificationCenter.default.post(name: .passwordStoreChangeDiscarded, object: nil)
return localCommits.count

View file

@ -10,6 +10,7 @@ import UIKit
protocol FillPasswordTableViewCellDelegate {
func generateAndCopyPassword()
func showHidePasswordSettings()
}
class FillPasswordTableViewCell: ContentTableViewCell {
@ -17,10 +18,17 @@ class FillPasswordTableViewCell: ContentTableViewCell {
@IBOutlet weak var contentTextField: UITextField!
var delegate: FillPasswordTableViewCellDelegate?
@IBOutlet weak var settingButton: UIButton!
@IBOutlet weak var generateButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
contentTextField.font = UIFont(name: Globals.passwordFonts, size: (contentTextField.font?.pointSize)!)
// Force aspect ratio of button images
settingButton.imageView?.contentMode = .scaleAspectFit
generateButton.imageView?.contentMode = .scaleAspectFit
}
override func setSelected(_ selected: Bool, animated: Bool) {
@ -33,6 +41,10 @@ class FillPasswordTableViewCell: ContentTableViewCell {
self.delegate?.generateAndCopyPassword()
}
@IBAction func showHidePasswordSettings() {
self.delegate?.showHidePasswordSettings()
}
// re-color
@IBAction func textFieldDidChange(_ sender: UITextField) {
contentTextField.attributedText = Utils.attributedPassword(plainPassword: sender.text ?? "")

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16F43c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12074.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -16,13 +16,13 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="90"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="89"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="89.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="password" textAlignment="natural" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="k0U-2N-YaX">
<rect key="frame" x="15" y="8" width="220" height="74"/>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="password" textAlignment="natural" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="k0U-2N-YaX" userLabel="Password">
<rect key="frame" x="16" y="8" width="231" height="74"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="38" id="qVX-ui-dIn"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="38" id="9gX-VT-F9P"/>
</constraints>
<nil key="textColor"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
@ -31,31 +31,52 @@
<action selector="textFieldDidChange:" destination="KGk-i7-Jjw" eventType="editingChanged" id="U0t-2B-JxY"/>
</connections>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" reversesTitleShadowWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hTh-ek-Xam">
<rect key="frame" x="243" y="-0.5" width="64" height="89.5"/>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SZJ-aY-45Y" userLabel="Setting">
<rect key="frame" x="275" y="8" width="30" height="73.5"/>
<constraints>
<constraint firstAttribute="width" constant="64" id="X1A-kc-qJy"/>
<constraint firstAttribute="width" constant="30" id="D9D-FC-ANz"/>
</constraints>
<state key="normal" title="Generate"/>
<inset key="contentEdgeInsets" minX="5" minY="10" maxX="5" maxY="10"/>
<state key="normal" image="HorizontalSettings"/>
<connections>
<action selector="showHidePasswordSettings" destination="KGk-i7-Jjw" eventType="touchUpInside" id="3I8-S0-qlo"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hTh-ek-Xam" userLabel="Generate">
<rect key="frame" x="245" y="8" width="30" height="73.5"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="l0l-7B-Tws"/>
</constraints>
<inset key="contentEdgeInsets" minX="5" minY="10" maxX="5" maxY="10"/>
<state key="normal" image="Refresh"/>
<connections>
<action selector="generatePassword:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="M61-hs-PCP"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="k0U-2N-YaX" secondAttribute="trailing" constant="77" id="3S1-gj-XVP" userLabel="trailingMargin = Content Text Field.trailing + 71"/>
<constraint firstItem="hTh-ek-Xam" firstAttribute="centerY" secondItem="k0U-2N-YaX" secondAttribute="centerY" id="Fls-do-khd"/>
<constraint firstItem="hTh-ek-Xam" firstAttribute="height" secondItem="H2p-sc-9uM" secondAttribute="height" id="Gok-dX-tQ5"/>
<constraint firstItem="hTh-ek-Xam" firstAttribute="leading" secondItem="k0U-2N-YaX" secondAttribute="trailing" constant="8" id="hsy-gb-ero" userLabel="Generate.leading = Content Text Field.trailing"/>
<constraint firstItem="k0U-2N-YaX" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="jKd-Xa-tr5"/>
<constraint firstItem="k0U-2N-YaX" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="7" id="lAm-D6-pir"/>
<constraint firstAttribute="bottomMargin" secondItem="k0U-2N-YaX" secondAttribute="bottom" constant="-1" id="pLC-H7-JTU" userLabel="bottomMargin = Content Text Field.bottom"/>
<constraint firstItem="SZJ-aY-45Y" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="5XP-xZ-YWY"/>
<constraint firstAttribute="trailingMargin" secondItem="SZJ-aY-45Y" secondAttribute="trailing" constant="7" id="5bp-bm-9vv"/>
<constraint firstAttribute="bottomMargin" secondItem="SZJ-aY-45Y" secondAttribute="bottom" id="CMF-1f-MdG"/>
<constraint firstItem="hTh-ek-Xam" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="OjQ-mD-CWV"/>
<constraint firstItem="SZJ-aY-45Y" firstAttribute="leading" secondItem="hTh-ek-Xam" secondAttribute="trailing" id="UlR-NB-9So"/>
<constraint firstAttribute="bottomMargin" secondItem="hTh-ek-Xam" secondAttribute="bottom" id="bvu-Q0-QAG"/>
<constraint firstAttribute="trailingMargin" secondItem="k0U-2N-YaX" secondAttribute="trailing" constant="65" id="fz0-HW-sCe"/>
<constraint firstAttribute="bottomMargin" secondItem="k0U-2N-YaX" secondAttribute="bottom" id="lRv-MC-Cru"/>
<constraint firstItem="k0U-2N-YaX" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="sUk-sk-JLT"/>
<constraint firstItem="k0U-2N-YaX" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="8" id="zGn-an-SlK"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="contentTextField" destination="k0U-2N-YaX" id="kQO-tR-Epp"/>
<outlet property="generateButton" destination="hTh-ek-Xam" id="MUa-qs-uw4"/>
<outlet property="settingButton" destination="SZJ-aY-45Y" id="G4T-04-NBD"/>
</connections>
<point key="canvasLocation" x="34" y="77"/>
</tableViewCell>
</objects>
<resources>
<image name="HorizontalSettings" width="25" height="25"/>
<image name="Refresh" width="32" height="32"/>
</resources>
</document>

View file

@ -27,6 +27,9 @@ class LabelTableViewCell: UITableViewCell {
weak var delegatePasswordTableView : PasswordDetailTableViewController?
var passwordDisplayButton: UIButton?
var buttons: UIView?
var cellData: LabelTableViewCellData? {
didSet {
titleLabel.text = cellData?.title ?? ""
@ -43,9 +46,11 @@ class LabelTableViewCell: UITableViewCell {
} else {
contentLabel.text = Globals.passwordDots
}
contentLabel.font = UIFont(name: Globals.passwordFonts, size: contentLabel.font.pointSize)
} else {
contentLabel.text = cellData?.content
}
updateButtons()
}
}
@ -58,6 +63,13 @@ class LabelTableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
}
override func layoutSubviews() {
super.layoutSubviews()
if buttons != nil {
self.accessoryView = buttons
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
@ -99,13 +111,26 @@ class LabelTableViewCell: UITableViewCell {
contentLabel.text = ""
}
isReveal = true
passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Invisible"), for: .normal)
}
func concealPassword(_ sender: Any?) {
contentLabel.text = Globals.passwordDots
isReveal = false
passwordDisplayButton?.setImage(#imageLiteral(resourceName: "Visible"), for: .normal)
}
func reversePasswordDisplay(_ sender: Any?) {
if isReveal {
// conceal
concealPassword(sender)
} else {
// reveal
revealPassword(sender)
}
}
func openLink(_ sender: Any?) {
// if isURLCell, passwordTableView should not be nil
delegatePasswordTableView!.openLink()
@ -115,4 +140,50 @@ class LabelTableViewCell: UITableViewCell {
// if isHOTPCell, passwordTableView should not be nil
delegatePasswordTableView!.getNextHOTP()
}
func updateButtons() {
passwordDisplayButton = nil
buttons = nil
// total width and height of a button
let height = min(self.bounds.height, 36.0)
let width = max(height * 0.8, Globals.tableCellButtonSize)
// margins (between button boundary and icon)
let marginY = max((height - Globals.tableCellButtonSize) / 2, 0.0)
let marginX = max((width - Globals.tableCellButtonSize) / 2, 0.0)
if isPasswordCell {
// password button
passwordDisplayButton = UIButton(type: .system)
passwordDisplayButton!.frame = CGRect(x: 0, y: 0, width: width, height: height)
passwordDisplayButton!.setImage(#imageLiteral(resourceName: "Visible"), for: .normal)
passwordDisplayButton!.imageView?.contentMode = .scaleAspectFit
passwordDisplayButton!.contentEdgeInsets = UIEdgeInsetsMake(marginY, marginX, marginY, marginX)
passwordDisplayButton!.addTarget(self, action: #selector(reversePasswordDisplay), for: UIControlEvents.touchUpInside)
buttons = passwordDisplayButton
} else if isHOTPCell {
// hotp button
let nextButton = UIButton(type: .system)
nextButton.frame = CGRect(x: 0, y: 0, width: width, height: height)
nextButton.setImage(#imageLiteral(resourceName: "Refresh"), for: .normal)
nextButton.imageView?.contentMode = .scaleAspectFit
nextButton.contentEdgeInsets = UIEdgeInsetsMake(marginY, marginX, marginY, marginX)
nextButton.addTarget(self, action: #selector(getNextHOTP), for: UIControlEvents.touchUpInside)
// password button
passwordDisplayButton = UIButton(type: .system)
passwordDisplayButton!.frame = CGRect(x: width, y: 0, width: width, height: height)
passwordDisplayButton!.setImage(#imageLiteral(resourceName: "Visible"), for: .normal)
passwordDisplayButton!.imageView?.contentMode = .scaleAspectFit
passwordDisplayButton!.contentEdgeInsets = UIEdgeInsetsMake(marginY, marginX, marginY, marginX)
passwordDisplayButton!.addTarget(self, action: #selector(reversePasswordDisplay), for: UIControlEvents.touchUpInside)
buttons = UIView()
buttons!.frame = CGRect(x: 0, y: 0, width: width * 2, height: height)
buttons!.addSubview(nextButton)
buttons!.addSubview(passwordDisplayButton!)
}
}
}

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16F43c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12074.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -16,17 +16,20 @@
<rect key="frame" x="0.0" y="0.0" width="333" height="52"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="333" height="51"/>
<rect key="frame" x="0.0" y="0.0" width="333" height="51.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yyr-cF-QN0">
<rect key="frame" x="15" y="26" width="310" height="17.5"/>
<rect key="frame" x="16" y="22.5" width="309" height="21"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="21" id="pgw-DF-LQa"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Dqz-7n-uEZ">
<rect key="frame" x="15" y="8" width="310" height="15"/>
<rect key="frame" x="16" y="8" width="309" height="11.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" red="0.0" green="0.47843137254901957" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
@ -34,7 +37,7 @@
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="Dqz-7n-uEZ" secondAttribute="trailing" id="228-VM-Z8Y"/>
<constraint firstItem="Dqz-7n-uEZ" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="7" id="CVm-uc-iVo"/>
<constraint firstItem="Dqz-7n-uEZ" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="8" id="CVm-uc-iVo"/>
<constraint firstAttribute="topMargin" secondItem="Dqz-7n-uEZ" secondAttribute="top" id="N4y-iT-CiY"/>
<constraint firstItem="yyr-cF-QN0" firstAttribute="leading" secondItem="Dqz-7n-uEZ" secondAttribute="leading" id="TpW-bu-QIx"/>
<constraint firstItem="yyr-cF-QN0" firstAttribute="width" secondItem="Dqz-7n-uEZ" secondAttribute="width" id="ZXa-fK-0Bg"/>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16F43c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12074.1"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -17,7 +17,7 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="84"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="83"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="83.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" placeholderIntrinsicWidth="52" placeholderIntrinsicHeight="52" translatesAutoresizingMaskIntoConstraints="NO" id="gKV-Cd-wIk">

View file

@ -1,27 +0,0 @@
//
// PasswordTableViewCell.swift
// pass
//
// Created by Mingshen Sun on 8/3/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
class PasswordWithFolderTableViewCell: UITableViewCell {
@IBOutlet weak var passwordLabel: UILabel!
@IBOutlet weak var folderLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

View file

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16E183b" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12074.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="passwordWithFolderTableViewCell" rowHeight="43" id="KGk-i7-Jjw" customClass="PasswordWithFolderTableViewCell" customModule="pass" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="42.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Password Entry" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="y5A-3S-tcq">
<rect key="frame" x="15" y="8" width="297" height="26.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Family &gt; Parents" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GEv-Cf-Cnx">
<rect key="frame" x="8" y="12.5" width="297" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="y5A-3S-tcq" firstAttribute="centerY" secondItem="GEv-Cf-Cnx" secondAttribute="centerY" id="Ev0-0v-v1c"/>
<constraint firstAttribute="trailingMargin" secondItem="GEv-Cf-Cnx" secondAttribute="trailing" constant="7" id="Hyk-Q8-5uU"/>
<constraint firstItem="y5A-3S-tcq" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="7" id="VDn-B1-Tyg"/>
<constraint firstItem="y5A-3S-tcq" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="e9F-C0-yGP"/>
<constraint firstAttribute="bottomMargin" secondItem="y5A-3S-tcq" secondAttribute="bottom" id="f52-hU-sEC"/>
<constraint firstAttribute="leadingMargin" secondItem="GEv-Cf-Cnx" secondAttribute="leading" id="rjm-bp-jT3"/>
<constraint firstAttribute="trailingMargin" secondItem="y5A-3S-tcq" secondAttribute="trailing" id="vsW-4d-EEl"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="folderLabel" destination="GEv-Cf-Cnx" id="HHd-LU-wYW"/>
<outlet property="passwordLabel" destination="y5A-3S-tcq" id="1OK-Io-6mP"/>
</connections>
<point key="canvasLocation" x="34" y="53.5"/>
</tableViewCell>
</objects>
</document>

View file

@ -58,6 +58,13 @@ class SliderTableViewCell: ContentTableViewCell {
slider.maximumValue = Float(maximumValue)
slider.value = Float(defaultValue)
valueLabel.text = String(defaultValue)
// "not editable"
if minimumValue == maximumValue {
titleLabel.textColor = UIColor.gray
valueLabel.textColor = UIColor.gray
slider.isUserInteractionEnabled = false
}
}
}

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16F43c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12074.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -20,7 +20,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="MwT-Jl-hhE">
<rect key="frame" x="60.5" y="22" width="205.5" height="31"/>
<rect key="frame" x="61.5" y="22" width="204.5" height="31"/>
<connections>
<action selector="handleSliderValueChange:" destination="KGk-i7-Jjw" eventType="valueChanged" id="WwM-ZE-yIB"/>
</connections>
@ -34,8 +34,8 @@
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="t7T-HC-hUd" userLabel="Title">
<rect key="frame" x="15" y="8" width="30" height="57.5"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="t7T-HC-hUd" userLabel="Title">
<rect key="frame" x="16" y="8" width="30" height="57.5"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="38" id="8Tz-Qo-Mkg"/>
</constraints>
@ -46,7 +46,7 @@
</subviews>
<constraints>
<constraint firstItem="MwT-Jl-hhE" firstAttribute="leading" secondItem="t7T-HC-hUd" secondAttribute="trailing" constant="17" id="Cc9-kV-nZf"/>
<constraint firstItem="t7T-HC-hUd" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="7" id="Kje-Zj-RaX"/>
<constraint firstItem="t7T-HC-hUd" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="8" id="Kje-Zj-RaX"/>
<constraint firstAttribute="trailingMargin" secondItem="GJP-Fj-VZt" secondAttribute="trailing" constant="7" id="LjD-JH-dRT"/>
<constraint firstItem="MwT-Jl-hhE" firstAttribute="centerY" secondItem="t7T-HC-hUd" secondAttribute="centerY" id="MHN-Vl-1le"/>
<constraint firstAttribute="bottomMargin" secondItem="GJP-Fj-VZt" secondAttribute="bottom" id="NmC-sS-9Yt"/>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16F43c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12074.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -16,11 +16,11 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="56"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="55"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="55.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8Ky-UZ-sLu">
<rect key="frame" x="15" y="8" width="297" height="39.5"/>
<rect key="frame" x="16" y="8" width="296" height="39.5"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="38" id="R1z-fU-Xr2"/>
</constraints>
@ -30,7 +30,7 @@
</textField>
</subviews>
<constraints>
<constraint firstItem="8Ky-UZ-sLu" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="7" id="3h4-9E-oDJ"/>
<constraint firstItem="8Ky-UZ-sLu" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="8" id="3h4-9E-oDJ"/>
<constraint firstItem="8Ky-UZ-sLu" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="FH4-4T-aaK"/>
<constraint firstAttribute="trailingMargin" secondItem="8Ky-UZ-sLu" secondAttribute="trailing" id="Nii-Cg-gC1"/>
<constraint firstAttribute="bottomMargin" secondItem="8Ky-UZ-sLu" secondAttribute="bottom" id="h72-l3-Sb3"/>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16E189a" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16F43c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
@ -20,7 +20,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xHX-Sh-1pR">
<rect key="frame" x="15" y="15" width="297" height="186.5"/>
<rect key="frame" x="12" y="16" width="300" height="184.5"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="120" id="Tvq-j8-Nvh"/>
@ -31,9 +31,9 @@
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="xHX-Sh-1pR" secondAttribute="trailing" id="LWS-JW-9dS"/>
<constraint firstItem="xHX-Sh-1pR" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="7" id="SRq-7t-Gyr"/>
<constraint firstAttribute="bottomMargin" secondItem="xHX-Sh-1pR" secondAttribute="bottom" constant="7" id="UPQ-jk-QJR"/>
<constraint firstItem="xHX-Sh-1pR" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" constant="7" id="gwb-2C-4wp"/>
<constraint firstItem="xHX-Sh-1pR" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="4" id="SRq-7t-Gyr"/>
<constraint firstAttribute="bottomMargin" secondItem="xHX-Sh-1pR" secondAttribute="bottom" constant="8" id="UPQ-jk-QJR"/>
<constraint firstItem="xHX-Sh-1pR" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" constant="8" id="gwb-2C-4wp"/>
</constraints>
</tableViewCellContentView>
<connections>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12106.1" systemVersion="16F43c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12074.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -16,17 +16,20 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="56"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="55"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="55.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HPC-rQ-39b">
<rect key="frame" x="15" y="8" width="297" height="15"/>
<rect key="frame" x="16" y="8" width="296" height="15"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8Ky-UZ-sLu">
<rect key="frame" x="15" y="26" width="297" height="21"/>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="123" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8Ky-UZ-sLu">
<rect key="frame" x="16" y="26" width="296" height="21"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="21" id="dHY-dw-mFe"/>
</constraints>
<nil key="textColor"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" returnKeyType="next"/>
@ -36,17 +39,17 @@
<constraint firstAttribute="bottomMargin" secondItem="8Ky-UZ-sLu" secondAttribute="bottom" id="11n-KG-zy6"/>
<constraint firstItem="8Ky-UZ-sLu" firstAttribute="trailing" secondItem="HPC-rQ-39b" secondAttribute="trailing" id="42z-vE-AsJ"/>
<constraint firstItem="8Ky-UZ-sLu" firstAttribute="leading" secondItem="HPC-rQ-39b" secondAttribute="leading" id="8F6-w6-BDY"/>
<constraint firstItem="HPC-rQ-39b" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="8" id="8ps-9g-9zG"/>
<constraint firstAttribute="trailingMargin" secondItem="HPC-rQ-39b" secondAttribute="trailing" id="Ayv-Bg-JxT"/>
<constraint firstItem="HPC-rQ-39b" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="topMargin" id="JN2-lI-0ZY"/>
<constraint firstItem="8Ky-UZ-sLu" firstAttribute="top" secondItem="HPC-rQ-39b" secondAttribute="bottom" constant="3" id="Pqs-gm-93j"/>
<constraint firstItem="HPC-rQ-39b" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" constant="7" id="vfA-M9-FEp"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="contentTextField" destination="8Ky-UZ-sLu" id="unz-5T-fuK"/>
<outlet property="titleLabel" destination="HPC-rQ-39b" id="VoV-gT-5lh"/>
</connections>
<point key="canvasLocation" x="34" y="60"/>
<point key="canvasLocation" x="0.0" y="61"/>
</tableViewCell>
</objects>
</document>

22
passTests/Info.plist Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>2</string>
</dict>
</plist>

35
passTests/passTests.swift Normal file
View file

@ -0,0 +1,35 @@
//
// passTests.swift
// passTests
//
// Created by Mingshen Sun on 24/3/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
import XCTest
class passTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}