Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f86210f5da | ||
|
|
e96f678236 | ||
|
|
8b9067ab4b | ||
|
|
5af383235a | ||
|
|
25b13dfb22 | ||
|
|
635692ef09 | ||
|
|
e6bab4e504 | ||
|
|
cd6b7e6051 | ||
|
|
b180202e07 | ||
|
|
f83f2af529 | ||
|
|
cf2c30cfe6 | ||
|
|
834e94ebd5 | ||
|
|
6d9be3fdfe | ||
|
|
07e55140ac | ||
|
|
fbbd08790d | ||
|
|
fcaa5d1d8c | ||
|
|
ea5be8efcb | ||
|
|
c8b4933c3d | ||
|
|
981ee171ae | ||
|
|
3ed6b93736 | ||
|
|
56a515c5ea | ||
|
|
15ac7a2dbf | ||
|
|
c5c7999753 | ||
|
|
bb58ed0169 | ||
|
|
4545a5c745 | ||
|
|
da8c7064fe | ||
|
|
d9bd604436 | ||
|
|
c99252809d | ||
|
|
d704f451a3 | ||
|
|
2c9ab5d153 | ||
|
|
d5d33da12f | ||
|
|
cbef1a611b | ||
|
|
0a1f215a1a | ||
|
|
907d2a8ca6 | ||
|
|
89f6e77f67 | ||
|
|
f2fb16a0b9 | ||
|
|
e3edd42b88 | ||
|
|
cc5d246d7d | ||
|
|
ca320de6d9 | ||
|
|
ae979d7240 | ||
|
|
eb1c443940 | ||
|
|
dadcefc9bf | ||
|
|
cdbaec9751 | ||
|
|
f48d480c77 |
79
README.md
79
README.md
@@ -15,13 +15,23 @@ To skip the intro and go straight to the information on how to use the code, [cl
|
||||
|
||||
Master Password is available for [📲 iOS](https://itunes.apple.com/app/id510296984), [🖥 macOS](https://ssl.masterpasswordapp.com/masterpassword-mac.zip), [📲 Android](https://ssl.masterpasswordapp.com/masterpassword-android.apk), [🖥 Desktop](https://ssl.masterpasswordapp.com/masterpassword-gui.jar), and [⌨ Console](https://ssl.masterpasswordapp.com/masterpassword-cli.tar.gz).
|
||||
|
||||
Master Password is also available from the following package managers: [macOS: Homebrew](https://brew.sh/). Get in touch if you are interested in adding Master Password to any other package managers.
|
||||
Master Password is also available from the following package managers: [macOS: Homebrew](https://brew.sh/) (`brew install mpw`).
|
||||
Get in touch if you are interested in adding Master Password to any other package managers.
|
||||
|
||||
There are many reasons for using Master Password instead of an ordinary password manager, read below for the details, but if you want my personal favourites, they would be:
|
||||
|
||||
- I don't need to worry about keeping backups of my countless authentication credentials.
|
||||
- I don't need to worry that when I travel, I might not have access to my passwords vault.
|
||||
- I don't need to trust an external party, proprietary code or a service to be online and stay online.
|
||||
- If I feel at risk of my device being stolen or confiscated, I can set a fake master password, delete my user or wipe it worry-free.
|
||||
|
||||
We also have a [Frequently Asked Questions](#faq).
|
||||
|
||||
|
||||
|
||||
## What is a password?
|
||||
|
||||
The "password". Somehow, passwords have become the default solution to authentication across the web. We've long since accepted this as the way things are, but let's stop to think for a moment about what passwords actually are:
|
||||
Ah, the "password". Somehow, passwords have become the default solution to authentication across the web. We've long since accepted this as the way things are, but let's stop to think for a moment about what passwords actually are:
|
||||
|
||||
A password is a secret that is known only to the party providing a service and the party that should be allowed access to this service.
|
||||
|
||||
@@ -69,12 +79,12 @@ Master Password is *not* a password manager. It does not store your website pas
|
||||
|
||||
## Benefits
|
||||
|
||||
- You don't need to come up with a secure password every time you make a new account - Master Password gives you the key for it.
|
||||
- You don't need to try to remember a password you created two years ago for that one account - Master Password just gives you the key for it.
|
||||
- You don't need to that you can't get into that account you made at work when you come home because you don't have your work passwords with you - Master Password is always available.
|
||||
- You don't need to try to keep password lists in sync or stored somewhere easily accessible - Master Password is always available.
|
||||
- You don't need to worry what you'll do if your computer dies or you need to log into your bank while you're in the airport transit zone - Master Password is always available.
|
||||
- You don't need to worry about your password manager website getting hacked, your phone getting duplicated, somebody taking a picture of your passwords book - Master Password keeps no records.
|
||||
- You don't need to think up a new strong password every time you make a new account - Master Password gives you the key for it.
|
||||
- You don't need to try remembering a password you created two years ago for that one account - Master Password just gives you the key for it.
|
||||
- You don't need to worry about getting into that account you made at work after you come home because you don't have your office passwords with you - Master Password is availale everywhere, even offline.
|
||||
- You don't need to try to keep password lists in sync or stored somewhere easily accessible - Master Password keys can be created anywhere.
|
||||
- You don't need to worry what you'll do if your computer dies or you need to log into your bank while you're in the airport transit zone - your Master Password keys are always available, even when starting empty.
|
||||
- You don't need to worry about your password manager website getting hacked, your phone getting duplicated, somebody taking a picture of your passwords book - Master Password stores no secrets.
|
||||
|
||||
|
||||
|
||||
@@ -94,6 +104,35 @@ We standardize `user-name` as your full name, `site-name` as the domain name of
|
||||
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
1. If I lose my master password and need to set a new one, will I need to change all of my site passwords?
|
||||
|
||||
Yes. If your master password is compromised, it is only sensible for you to change all of your site passwords. Just like if you lose the keys in your pocket, you'll have to change all the locks they open. Master Password effectively enforces this security practice.
|
||||
|
||||
2. But what if I just forget my master password or I just want to change it to something else?
|
||||
|
||||
Sorry, still yes. Your master password is the secret component to your Master Password identity. If it changes, your identity changes. I wholly encourage you to think very carefully about what makes for a really memorable and good master password before just diving in with something lazy. A short phrase works great, eg. `banana coloured duckling`.
|
||||
|
||||
3. Doesn't this mean an attacker can reverse my master password from any of my site passwords?
|
||||
|
||||
Technically, yes. Practically, no.
|
||||
|
||||
You could argue that site passwords are "breadcrumbs" of your master password, but the same argument would suggest encrypted messages are breadcrumbs to the encryption key. Encryption works because it is computationally unfeasible to "guess" the encryption key that made the encrypted message, just like Master Password works because it is computationally unfeasible to "guess" your master password that made the site password.
|
||||
|
||||
4. The second step is just a HMAC-SHA-256, doesn't that make the SCRYPT completely pointless?
|
||||
|
||||
No. They are used for different reasons and one is not weaker than the other.
|
||||
|
||||
HMAC-SHA-256 is much faster to compute than SCRYPT, which leads some people to think "all an attacker needs to do is brute-force the SHA and ignore the SCRYPT". The reality is that the HMAC-SHA-256 guards a 64-byte authentication key (the `master-key`) which makes the search space for brute-forcing the HMAC wildly too large to compute.
|
||||
The `master-password` on the other hand, is only a simple phrase, which means its search space is much smaller. This is why it is guarded by a much tougher SCRYPT operation.
|
||||
|
||||
5. I have another question.
|
||||
|
||||
Please don't hesitate to [get in touch](#support), we're more than happy to answer all your Master Password questions. Any problems or suggestions can be reported [as GitHub issues](https://github.com/Lyndir/MasterPassword/issues).
|
||||
|
||||
|
||||
|
||||
|
||||
# Source Code
|
||||
|
||||
@@ -146,6 +185,10 @@ Go into the `gradle` directory and run `./gradlew build`. All Java components w
|
||||
- `platform-android/build/outputs/apk`:
|
||||
contains the Android application package. Install it on your Android device.
|
||||
|
||||
Note that in order to build the Android application, you will need to have the Android SDK installed and either have the environment variable `ANDROID_HOME` set to its location or a `gradle/local.properties` file with its location, eg. (for Homebrew users who installed the SDK using `brew install android-sdk`):
|
||||
|
||||
sdk.dir=/usr/local/opt/android-sdk
|
||||
|
||||
|
||||
### Native CLI
|
||||
|
||||
@@ -158,7 +201,7 @@ For example:
|
||||
./build && sudo ./install
|
||||
mpw -h
|
||||
|
||||
Normally, this is all you need to do, however note that there are a few dependencies that need to be met, depending on which targets you are building:
|
||||
Normally, this is all you need to do, however note that there are a few dependencies that need to be met, depending on which targets you are building (by default, only the `mpw` target is built):
|
||||
|
||||
- `mpw`
|
||||
|
||||
@@ -176,28 +219,24 @@ This tool compares the performance of a few cryptographic algorithms, including
|
||||
|
||||
This tool runs a suite of tests to ensure the correct passwords are being generated by the algorithm under various circumstances. The test suite is declared in `mpw-tests.xml` which needs to exist in the current working directory when running the tool. In addition, `libxml2` is used to parse the file, so this target depends on you having it installed when running `./build`.
|
||||
|
||||
Finally, there are a few different ways you can modify the build process:
|
||||
|
||||
- You can change the targets that should be built. By default, only `mpw` is built. These are the available targets:
|
||||
- `mpw`: This is the standard command-line `mpw` tool which implements all Master Password features.
|
||||
- `mpw-tests`: This is a tool to perform the standard tests script on the `mpw` implementation.
|
||||
- `mpw-bench`: This is a tool to run a benchmark on the `mpw` implementation, comparing it to the performance of other algorithms.
|
||||
- You can specify custom arguments to the compiler, pass them as arguments to the build script.
|
||||
- The build process involves some optionals, they can by toggled from their default setting by passing variables:
|
||||
- `mpw_color`: [default: 1] Colorized Identicon, depends on
|
||||
Finally, there are a few different ways you can modify the build process.
|
||||
|
||||
To change the targets to build, use:
|
||||
To build additional targets, set the `targets` environment variable:
|
||||
|
||||
targets='mpw mpw-tests' ./build
|
||||
|
||||
To add a library search path, use:
|
||||
To pass additional compiler arguments, eg. add a library search path, pass them as arguments to the script:
|
||||
|
||||
./build -L/usr/local/lib
|
||||
|
||||
Change an optional feature:
|
||||
There are a few toggleable features, to change them, pass them as environment variables:
|
||||
|
||||
mpw_color=0 ./build
|
||||
|
||||
Currently, there is only one toggleable feature:
|
||||
|
||||
- `mpw_color`: [default: 1] Colorized Identicon, depends on `ncurses-dev`.
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
2
platform-darwin/External/InAppSettingsKit
vendored
2
platform-darwin/External/InAppSettingsKit
vendored
Submodule platform-darwin/External/InAppSettingsKit updated: b58b72563a...2dcb598d18
2
platform-darwin/External/Pearl
vendored
2
platform-darwin/External/Pearl
vendored
Submodule platform-darwin/External/Pearl updated: d02256465a...af1cf4cba3
@@ -12,13 +12,16 @@
|
||||
93D3922A53E41A54832E90D9 /* PearlOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FADEB325D8D54A957D /* PearlOverlay.m */; };
|
||||
93D39262A8A97DB748213309 /* PearlEMail.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393BB973253D4BAAC84AA /* PearlEMail.m */; };
|
||||
93D3928D629EA563F9EC4909 /* NSPersistentStore+PearlMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399C2F3D48E57C4803BDC /* NSPersistentStore+PearlMigration.m */; };
|
||||
93D392A33CCE85431E910C7B /* NSOrderedSetOrArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39789AAF49338F8AC8B02 /* NSOrderedSetOrArray.m */; };
|
||||
93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */; };
|
||||
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
|
||||
93D392FD5E2052F7D7DB3774 /* NSString+MPMarkDown.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */; };
|
||||
93D3932889B6B4206E66A6D6 /* PearlEMail.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */; };
|
||||
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39BAA71DE51B4D8A1286C /* MPCell.m */; };
|
||||
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3957D76F71A652716EECC /* MPStoreViewController.m */; };
|
||||
93D393AA69A1193401160418 /* UIView+AlphaScale.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39488AB33616661725929 /* UIView+AlphaScale.m */; };
|
||||
93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; };
|
||||
93D3942C1B117EE4851AA7B6 /* UIView+Visible.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3952910EDB8E0EBC94BA9 /* UIView+Visible.m */; };
|
||||
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; };
|
||||
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; };
|
||||
93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399B36CDB2004D7C51391 /* MPMessageViewController.m */; };
|
||||
@@ -27,6 +30,7 @@
|
||||
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; };
|
||||
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B381350802A194BF332 /* MPAvatarCell.m */; };
|
||||
93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39FD9623E8D5571C0AEB3 /* MPAlgorithmV3.m */; };
|
||||
93D3959696396A91961C6148 /* UIView+AlphaScale.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D392D76C091DEA3319F11D /* UIView+AlphaScale.h */; };
|
||||
93D395B715D15F2B56F2A2EE /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D392C5A6572DB0EB5B82C8 /* mpw-types.c */; };
|
||||
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
|
||||
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */; };
|
||||
@@ -38,11 +42,11 @@
|
||||
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */; };
|
||||
93D399246DC90F50913A1287 /* UIResponder+PearlFirstResponder.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A1DDFA09AE2E14D26DC /* UIResponder+PearlFirstResponder.m */; };
|
||||
93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; };
|
||||
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; };
|
||||
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = 93D396C311C3725870343EE0 /* mpw-util.c */; };
|
||||
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D395105935859D71679931 /* MPOverlayViewController.m */; };
|
||||
93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */; };
|
||||
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */; };
|
||||
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */; };
|
||||
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadItems.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */; };
|
||||
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; };
|
||||
93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; };
|
||||
93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */; };
|
||||
@@ -51,8 +55,9 @@
|
||||
93D39B842AB9A5D072810D76 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398C95847261903D781D3 /* NSError+PearlFullDescription.h */; };
|
||||
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; };
|
||||
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */; };
|
||||
93D39BFB5F5F9337F6565DE3 /* UIView+Visible.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39B7B765546B1F1900CB7 /* UIView+Visible.h */; };
|
||||
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
|
||||
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */; };
|
||||
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadItems.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadItems.m */; };
|
||||
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; };
|
||||
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39CC01630D0421205C4C4 /* MPNavigationController.m */; };
|
||||
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
|
||||
@@ -65,6 +70,36 @@
|
||||
DA071BF4190187FE00179766 /* empty.png in Resources */ = {isa = PBXBuildFile; fileRef = DA071BF2190187FE00179766 /* empty.png */; };
|
||||
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */; };
|
||||
DA0979171E9A81EE00F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979161E9A81EE00F0BFE8 /* libsodium.a */; };
|
||||
DA0CC5291EAB99BA009A8ED9 /* IASKAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5071EAB99BA009A8ED9 /* IASKAppSettingsViewController.m */; };
|
||||
DA0CC52A1EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5091EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m */; };
|
||||
DA0CC52B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC50B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m */; };
|
||||
DA0CC52C1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC50D1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m */; };
|
||||
DA0CC52D1EAB99BA009A8ED9 /* IASKSettingsReader.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5111EAB99BA009A8ED9 /* IASKSettingsReader.m */; };
|
||||
DA0CC52E1EAB99BA009A8ED9 /* IASKSettingsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5131EAB99BA009A8ED9 /* IASKSettingsStore.m */; };
|
||||
DA0CC52F1EAB99BA009A8ED9 /* IASKSettingsStoreFile.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5151EAB99BA009A8ED9 /* IASKSettingsStoreFile.m */; };
|
||||
DA0CC5301EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5171EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m */; };
|
||||
DA0CC5311EAB99BA009A8ED9 /* IASKSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5191EAB99BA009A8ED9 /* IASKSpecifier.m */; };
|
||||
DA0CC5321EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC51C1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m */; };
|
||||
DA0CC5331EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC51E1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m */; };
|
||||
DA0CC5341EAB99BA009A8ED9 /* IASKSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5201EAB99BA009A8ED9 /* IASKSlider.m */; };
|
||||
DA0CC5351EAB99BA009A8ED9 /* IASKSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5221EAB99BA009A8ED9 /* IASKSwitch.m */; };
|
||||
DA0CC5361EAB99BA009A8ED9 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5241EAB99BA009A8ED9 /* IASKTextField.m */; };
|
||||
DA0CC5371EAB99BA009A8ED9 /* IASKTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */; };
|
||||
DA0CC5381EAB99BA009A8ED9 /* IASKTextViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5281EAB99BA009A8ED9 /* IASKTextViewCell.m */; };
|
||||
DA0CC53B1EB57B5C009A8ED9 /* Fabric.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */; };
|
||||
DA0CC5411EB57BD4009A8ED9 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */; };
|
||||
DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA141191922FED80032B392 /* Crashlytics.framework */; };
|
||||
DA0CC54E1EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5441EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld */; };
|
||||
DA0CC58C1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5791EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m */; };
|
||||
DA0CC58D1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC57B1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m */; };
|
||||
DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC57D1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m */; };
|
||||
DA0CC58F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC57F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m */; };
|
||||
DA0CC5901EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5811EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m */; };
|
||||
DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5831EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m */; };
|
||||
DA0CC5921EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5851EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m */; };
|
||||
DA0CC5931EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5871EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m */; };
|
||||
DA0CC5941EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */; };
|
||||
DA0CC5951EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */; };
|
||||
DA24EBAE19DAD08900FF010B /* tip_basic_black_top.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38941711E29700CF925C /* tip_basic_black_top.png */; };
|
||||
DA24EBAF19DAD08C00FF010B /* tip_basic_black_top@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38951711E29700CF925C /* tip_basic_black_top@2x.png */; };
|
||||
DA24EBE819DAD6DE00FF010B /* Icon-320.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBE619DAD6DE00FF010B /* Icon-320.png */; };
|
||||
@@ -72,8 +107,8 @@
|
||||
DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBA19DAD4D000FF010B /* Icon-Small.png */; };
|
||||
DA24EBEB19DAD6EE00FF010B /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBB19DAD4D000FF010B /* Icon-Small@2x.png */; };
|
||||
DA24EBEC19DAD6EE00FF010B /* Icon-Small@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA24EBBC19DAD4D000FF010B /* Icon-Small@3x.png */; };
|
||||
DA250A17195665A100AC23F1 /* UITableView+PearlReloadFromArray.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A13195665A100AC23F1 /* UITableView+PearlReloadFromArray.m */; };
|
||||
DA250A18195665A100AC23F1 /* UITableView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A14195665A100AC23F1 /* UITableView+PearlReloadFromArray.h */; };
|
||||
DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */; };
|
||||
DA250A18195665A100AC23F1 /* UITableView+PearlReloadItems.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */; };
|
||||
DA250A19195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m in Sources */ = {isa = PBXBuildFile; fileRef = DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */; };
|
||||
DA250A1A195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h in Headers */ = {isa = PBXBuildFile; fileRef = DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */; };
|
||||
DA25C5F8197AFFB40046CDCF /* icon_tools.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD384C1711E29700CF925C /* icon_tools.png */; };
|
||||
@@ -92,7 +127,6 @@
|
||||
DA29993219C9132F00AF7DF1 /* thumb_generated_login@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA29993119C9132F00AF7DF1 /* thumb_generated_login@3x.png */; };
|
||||
DA29993319C9214600AF7DF1 /* icon_star-hollow.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD382A1711E29600CF925C /* icon_star-hollow.png */; };
|
||||
DA29993419C9214600AF7DF1 /* icon_star-hollow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD382B1711E29600CF925C /* icon_star-hollow@2x.png */; };
|
||||
DA2C3D611BD95EEE001137B3 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D601BD95EEE001137B3 /* Fabric.framework */; };
|
||||
DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D621BD96126001137B3 /* libc++.tbd */; };
|
||||
DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA2C3D641BD9612F001137B3 /* libz.tbd */; };
|
||||
DA2CA4DD18D28859007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4D918D28859007798F8 /* NSArray+Pearl.m */; };
|
||||
@@ -106,10 +140,6 @@
|
||||
DA30E9D415722EF400A68B4C /* Pearl-UIKit.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */; };
|
||||
DA30E9D715723E6900A68B4C /* PearlLazy.h in Headers */ = {isa = PBXBuildFile; fileRef = DA30E9D515723E6900A68B4C /* PearlLazy.h */; };
|
||||
DA30E9D815723E6900A68B4C /* PearlLazy.m in Sources */ = {isa = PBXBuildFile; fileRef = DA30E9D615723E6900A68B4C /* PearlLazy.m */; };
|
||||
DA32CFF019CF1C8F004F3F0E /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */; };
|
||||
DA32CFF119CF1C8F004F3F0E /* MPStoredSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */; };
|
||||
DA32CFF319CF1C8F004F3F0E /* MPSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */; };
|
||||
DA32CFF419CF1C8F004F3F0E /* MPGeneratedSiteEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */; };
|
||||
DA32D00919CF5C55004F3F0E /* icon_question.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37FE1711E29600CF925C /* icon_question.png */; };
|
||||
DA32D00A19CF5C55004F3F0E /* icon_question@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37FF1711E29600CF925C /* icon_question@2x.png */; };
|
||||
DA32D01A19D046E1004F3F0E /* PearlFixedTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */; };
|
||||
@@ -127,7 +157,6 @@
|
||||
DA32D05019D2F59B004F3F0E /* meter_fuel.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D04D19D2F59B004F3F0E /* meter_fuel.png */; };
|
||||
DA32D05119D3D107004F3F0E /* icon_meter.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37BA1711E29600CF925C /* icon_meter.png */; };
|
||||
DA32D05219D3D107004F3F0E /* icon_meter@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37BB1711E29600CF925C /* icon_meter@2x.png */; };
|
||||
DA32D05519D741DC004F3F0E /* MPSiteQuestionEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */; };
|
||||
DA32D07A19D7D784004F3F0E /* background@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D07719D7D784004F3F0E /* background@3x.png */; };
|
||||
DA32D07B19D7D784004F3F0E /* background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D07819D7D784004F3F0E /* background@2x.png */; };
|
||||
DA32D07C19D7D784004F3F0E /* background.png in Resources */ = {isa = PBXBuildFile; fileRef = DA32D07919D7D784004F3F0E /* background.png */; };
|
||||
@@ -143,7 +172,6 @@
|
||||
DA45224A190628A1008F650A /* icon_wrench@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD386B1711E29700CF925C /* icon_wrench@2x.png */; };
|
||||
DA45224B190628B2008F650A /* icon_gear.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37821711E29500CF925C /* icon_gear.png */; };
|
||||
DA45224C190628B2008F650A /* icon_gear@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD37831711E29500CF925C /* icon_gear@2x.png */; };
|
||||
DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAA141191922FED80032B392 /* Crashlytics.framework */; };
|
||||
DA4DA1D91564471A00F6F596 /* libjrswizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6326C148680650075AEA5 /* libjrswizzle.a */; };
|
||||
DA4DA1DA1564471F00F6F596 /* libuicolor-utilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC6325D1486805C0075AEA5 /* libuicolor-utilities.a */; };
|
||||
DA5A09DF171A70E4005284AB /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5A09DD171A70E4005284AB /* play.png */; };
|
||||
@@ -180,7 +208,6 @@
|
||||
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA945C8617E3F3FD0053236B /* Images.xcassets */; };
|
||||
DA95B50C1C476B6A0067F5EF /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA95B50B1C476B6A0067F5EF /* LocalAuthentication.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA95B50E1C4776F00067F5EF /* NSMutableSet+Pearl.m */; };
|
||||
DA95B5191C477DB50067F5EF /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA95B5101C477DB50067F5EF /* MasterPassword.xcdatamodeld */; };
|
||||
DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA95D5F014DF0B1E008D1B94 /* MessageUI.framework */; };
|
||||
DAA141201922FF020032B392 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DAA1411C1922FF020032B392 /* PearlTween.m */; };
|
||||
DAA141211922FF020032B392 /* PearlTween.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA1411D1922FF020032B392 /* PearlTween.h */; };
|
||||
@@ -313,7 +340,6 @@
|
||||
DAC77CAE148291A600BCF976 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
|
||||
DAC8DF47192831E100BA7D71 /* icon_key.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379A1711E29600CF925C /* icon_key.png */; };
|
||||
DAC8DF48192831E100BA7D71 /* icon_key@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD379B1711E29600CF925C /* icon_key@2x.png */; };
|
||||
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA269A1705DF81002C6C22 /* Crashlytics.plist */; };
|
||||
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
|
||||
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
|
||||
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; };
|
||||
@@ -342,20 +368,6 @@
|
||||
DAF4EF50190A81E400023C90 /* NSManagedObject+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF4EF4E190A81E400023C90 /* NSManagedObject+Pearl.m */; };
|
||||
DAF4EF51190A81E400023C90 /* NSManagedObject+Pearl.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF4EF4F190A81E400023C90 /* NSManagedObject+Pearl.h */; };
|
||||
DAFC5656172C573B00CB5CC5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA5BFA4A147E415C00F98B1E /* Foundation.framework */; };
|
||||
DAFC5683172C57EC00CB5CC5 /* IASKAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5665172C57EC00CB5CC5 /* IASKAppSettingsViewController.m */; };
|
||||
DAFC5684172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5667172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m */; };
|
||||
DAFC5685172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5669172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m */; };
|
||||
DAFC5686172C57EC00CB5CC5 /* IASKSettingsReader.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC566D172C57EC00CB5CC5 /* IASKSettingsReader.m */; };
|
||||
DAFC5687172C57EC00CB5CC5 /* IASKSettingsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC566F172C57EC00CB5CC5 /* IASKSettingsStore.m */; };
|
||||
DAFC5688172C57EC00CB5CC5 /* IASKSettingsStoreFile.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5671172C57EC00CB5CC5 /* IASKSettingsStoreFile.m */; };
|
||||
DAFC5689172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5673172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m */; };
|
||||
DAFC568A172C57EC00CB5CC5 /* IASKSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5675172C57EC00CB5CC5 /* IASKSpecifier.m */; };
|
||||
DAFC568B172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5678172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m */; };
|
||||
DAFC568C172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC567A172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m */; };
|
||||
DAFC568D172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC567C172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m */; };
|
||||
DAFC568E172C57EC00CB5CC5 /* IASKSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC567E172C57EC00CB5CC5 /* IASKSlider.m */; };
|
||||
DAFC568F172C57EC00CB5CC5 /* IASKSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5680172C57EC00CB5CC5 /* IASKSwitch.m */; };
|
||||
DAFC5690172C57EC00CB5CC5 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFC5682172C57EC00CB5CC5 /* IASKTextField.m */; };
|
||||
DAFE4A1315039824003ABA7C /* NSObject+PearlExport.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */; };
|
||||
DAFE4A1415039824003ABA7C /* NSObject+PearlExport.m in Sources */ = {isa = PBXBuildFile; fileRef = DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */; };
|
||||
DAFE4A1515039824003ABA7C /* NSString+PearlNSArrayFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = DAFE45DA15039823003ABA7C /* NSString+PearlNSArrayFormat.h */; };
|
||||
@@ -458,7 +470,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = "<group>"; };
|
||||
93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = "<group>"; };
|
||||
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadFromArray.m"; sourceTree = "<group>"; };
|
||||
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadItems.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadItems.m"; sourceTree = "<group>"; };
|
||||
93D390A3B351FEF1B9EDAB56 /* mpw-algorithm_v2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v2.c"; sourceTree = "<group>"; };
|
||||
93D390A99850139D0FF0211E /* mpw-algorithm_v0.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v0.c"; sourceTree = "<group>"; };
|
||||
93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = "<group>"; };
|
||||
@@ -469,11 +481,12 @@
|
||||
93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppSettingsViewController.m; sourceTree = "<group>"; };
|
||||
93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = "<group>"; };
|
||||
93D391AA32F24290C424438E /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; };
|
||||
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadFromArray.h"; sourceTree = "<group>"; };
|
||||
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadItems.h"; sourceTree = "<group>"; };
|
||||
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRootSegue.h; sourceTree = "<group>"; };
|
||||
93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = "<group>"; };
|
||||
93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = "<group>"; };
|
||||
93D392C5A6572DB0EB5B82C8 /* mpw-types.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-types.c"; sourceTree = "<group>"; };
|
||||
93D392D76C091DEA3319F11D /* UIView+AlphaScale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+AlphaScale.h"; sourceTree = "<group>"; };
|
||||
93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = "<group>"; };
|
||||
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
|
||||
93D393BB973253D4BAAC84AA /* PearlEMail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlEMail.m; sourceTree = "<group>"; };
|
||||
@@ -481,8 +494,10 @@
|
||||
93D394077F8FAB8167647187 /* Twitter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Twitter.framework; path = System/Library/Frameworks/Twitter.framework; sourceTree = SDKROOT; };
|
||||
93D3942A356B639724157982 /* PearlOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlOverlay.h; sourceTree = "<group>"; };
|
||||
93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+PearlFirstResponder.h"; sourceTree = "<group>"; };
|
||||
93D39488AB33616661725929 /* UIView+AlphaScale.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+AlphaScale.m"; sourceTree = "<group>"; };
|
||||
93D394D73F5BC92297CE8D7B /* MPAlgorithmV3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAlgorithmV3.h; sourceTree = "<group>"; };
|
||||
93D395105935859D71679931 /* MPOverlayViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPOverlayViewController.m; sourceTree = "<group>"; };
|
||||
93D3952910EDB8E0EBC94BA9 /* UIView+Visible.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Visible.m"; sourceTree = "<group>"; };
|
||||
93D3956915634581E737B38C /* PearlNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlNavigationController.m; sourceTree = "<group>"; };
|
||||
93D3957D76F71A652716EECC /* MPStoreViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoreViewController.m; sourceTree = "<group>"; };
|
||||
93D3969393A3A46BD27D7078 /* mpw-algorithm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm.c"; sourceTree = "<group>"; };
|
||||
@@ -493,6 +508,7 @@
|
||||
93D3971FE104BB4052484151 /* MPUsersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUsersViewController.h; sourceTree = "<group>"; };
|
||||
93D39730673227EFF6DEFF19 /* MPSetupViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSetupViewController.h; sourceTree = "<group>"; };
|
||||
93D3977321EB249981821AB0 /* UITextView+PearlAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+PearlAttributes.m"; sourceTree = "<group>"; };
|
||||
93D39789AAF49338F8AC8B02 /* NSOrderedSetOrArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSOrderedSetOrArray.m; sourceTree = "<group>"; };
|
||||
93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLogsViewController.m; sourceTree = "<group>"; };
|
||||
93D397F4BAFFF7CF3F1B21A4 /* NSPersistentStore+PearlMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPersistentStore+PearlMigration.h"; sourceTree = "<group>"; };
|
||||
93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlNavigationController.h; sourceTree = "<group>"; };
|
||||
@@ -521,6 +537,7 @@
|
||||
93D39B1D8177A86C5B9EDDE3 /* PearlUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlUICollectionView.h; sourceTree = "<group>"; };
|
||||
93D39B381350802A194BF332 /* MPAvatarCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAvatarCell.m; sourceTree = "<group>"; };
|
||||
93D39B455A71EC98C749E623 /* MPOverlayViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPOverlayViewController.h; sourceTree = "<group>"; };
|
||||
93D39B7B765546B1F1900CB7 /* UIView+Visible.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Visible.h"; sourceTree = "<group>"; };
|
||||
93D39BAA71DE51B4D8A1286C /* MPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCell.m; sourceTree = "<group>"; };
|
||||
93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+MPMarkDown.m"; path = "iOS/NSString+MPMarkDown.m"; sourceTree = "<group>"; };
|
||||
93D39C426E03358384018E85 /* MPAnswersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAnswersViewController.m; sourceTree = "<group>"; };
|
||||
@@ -542,6 +559,7 @@
|
||||
93D39F556F2F142740A65E59 /* MPWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPWebViewController.h; sourceTree = "<group>"; };
|
||||
93D39F7C9F47BF6387FBC5C3 /* PearlEMail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlEMail.h; sourceTree = "<group>"; };
|
||||
93D39F9106F2CCFB94283188 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = "<group>"; };
|
||||
93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSOrderedSetOrArray.h; sourceTree = "<group>"; };
|
||||
93D39FD9623E8D5571C0AEB3 /* MPAlgorithmV3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV3.m; sourceTree = "<group>"; };
|
||||
DA04E33D14B1E70400ECA4F3 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
|
||||
DA071BF1190187FE00179766 /* empty@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "empty@2x.png"; sourceTree = "<group>"; };
|
||||
@@ -605,6 +623,85 @@
|
||||
DA0979131E9A81EE00F0BFE8 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
|
||||
DA0979141E9A81EE00F0BFE8 /* sodium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sodium.h; sourceTree = "<group>"; };
|
||||
DA0979161E9A81EE00F0BFE8 /* libsodium.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsodium.a; sourceTree = "<group>"; };
|
||||
DA0CC4F61EAB99BA009A8ED9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4F71EAB99BA009A8ED9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4F81EAB99BA009A8ED9 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4F91EAB99BA009A8ED9 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FA1EAB99BA009A8ED9 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FB1EAB99BA009A8ED9 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FC1EAB99BA009A8ED9 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FD1EAB99BA009A8ED9 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FE1EAB99BA009A8ED9 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC4FF1EAB99BA009A8ED9 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/IASKLocalizable.strings"; sourceTree = "<group>"; };
|
||||
DA0CC5001EAB99BA009A8ED9 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5011EAB99BA009A8ED9 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5021EAB99BA009A8ED9 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5031EAB99BA009A8ED9 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5041EAB99BA009A8ED9 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/IASKLocalizable.strings; sourceTree = "<group>"; };
|
||||
DA0CC5061EAB99BA009A8ED9 /* IASKAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKAppSettingsViewController.h; sourceTree = "<group>"; };
|
||||
DA0CC5071EAB99BA009A8ED9 /* IASKAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKAppSettingsViewController.m; sourceTree = "<group>"; };
|
||||
DA0CC5081EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKAppSettingsWebViewController.h; sourceTree = "<group>"; };
|
||||
DA0CC5091EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKAppSettingsWebViewController.m; sourceTree = "<group>"; };
|
||||
DA0CC50A1EAB99BA009A8ED9 /* IASKMultipleValueSelection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKMultipleValueSelection.h; sourceTree = "<group>"; };
|
||||
DA0CC50B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKMultipleValueSelection.m; sourceTree = "<group>"; };
|
||||
DA0CC50C1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSpecifierValuesViewController.h; sourceTree = "<group>"; };
|
||||
DA0CC50D1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSpecifierValuesViewController.m; sourceTree = "<group>"; };
|
||||
DA0CC50E1EAB99BA009A8ED9 /* IASKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKViewController.h; sourceTree = "<group>"; };
|
||||
DA0CC5101EAB99BA009A8ED9 /* IASKSettingsReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsReader.h; sourceTree = "<group>"; };
|
||||
DA0CC5111EAB99BA009A8ED9 /* IASKSettingsReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsReader.m; sourceTree = "<group>"; };
|
||||
DA0CC5121EAB99BA009A8ED9 /* IASKSettingsStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStore.h; sourceTree = "<group>"; };
|
||||
DA0CC5131EAB99BA009A8ED9 /* IASKSettingsStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStore.m; sourceTree = "<group>"; };
|
||||
DA0CC5141EAB99BA009A8ED9 /* IASKSettingsStoreFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStoreFile.h; sourceTree = "<group>"; };
|
||||
DA0CC5151EAB99BA009A8ED9 /* IASKSettingsStoreFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStoreFile.m; sourceTree = "<group>"; };
|
||||
DA0CC5161EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStoreUserDefaults.h; sourceTree = "<group>"; };
|
||||
DA0CC5171EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStoreUserDefaults.m; sourceTree = "<group>"; };
|
||||
DA0CC5181EAB99BA009A8ED9 /* IASKSpecifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSpecifier.h; sourceTree = "<group>"; };
|
||||
DA0CC5191EAB99BA009A8ED9 /* IASKSpecifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSpecifier.m; sourceTree = "<group>"; };
|
||||
DA0CC51B1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSSliderSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DA0CC51C1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSSliderSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DA0CC51D1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSTextFieldSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DA0CC51E1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSTextFieldSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DA0CC51F1EAB99BA009A8ED9 /* IASKSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSlider.h; sourceTree = "<group>"; };
|
||||
DA0CC5201EAB99BA009A8ED9 /* IASKSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSlider.m; sourceTree = "<group>"; };
|
||||
DA0CC5211EAB99BA009A8ED9 /* IASKSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSwitch.h; sourceTree = "<group>"; };
|
||||
DA0CC5221EAB99BA009A8ED9 /* IASKSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSwitch.m; sourceTree = "<group>"; };
|
||||
DA0CC5231EAB99BA009A8ED9 /* IASKTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKTextField.h; sourceTree = "<group>"; };
|
||||
DA0CC5241EAB99BA009A8ED9 /* IASKTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextField.m; sourceTree = "<group>"; };
|
||||
DA0CC5251EAB99BA009A8ED9 /* IASKTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKTextView.h; sourceTree = "<group>"; };
|
||||
DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextView.m; sourceTree = "<group>"; };
|
||||
DA0CC5271EAB99BA009A8ED9 /* IASKTextViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKTextViewCell.h; sourceTree = "<group>"; };
|
||||
DA0CC5281EAB99BA009A8ED9 /* IASKTextViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextViewCell.m; sourceTree = "<group>"; };
|
||||
DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Fabric.plist; sourceTree = "<group>"; };
|
||||
DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = "<group>"; };
|
||||
DA0CC5451EB6AD0E009A8ED9 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5461EB6AD0E009A8ED9 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5471EB6AD0E009A8ED9 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5481EB6AD0E009A8ED9 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5491EB6AD0E009A8ED9 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC54A1EB6AD0E009A8ED9 /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC54B1EB6AD0E009A8ED9 /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC54C1EB6AD0E009A8ED9 /* MasterPassword 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC54D1EB6AD0E009A8ED9 /* MasterPassword 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 9.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5781EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPGeneratedSiteEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC5791EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPGeneratedSiteEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC57A1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPGeneratedSiteEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC57B1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPGeneratedSiteEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA0CC57C1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteQuestionEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC57D1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteQuestionEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC57E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteQuestionEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC57F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteQuestionEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA0CC5801EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPStoredSiteEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC5811EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPStoredSiteEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC5821EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPStoredSiteEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC5831EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPStoredSiteEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA0CC5841EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPUserEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC5851EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPUserEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC5861EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPUserEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC5871EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPUserEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA0CC5881EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteEntity+CoreDataClass.h"; sourceTree = "<group>"; };
|
||||
DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataClass.m"; sourceTree = "<group>"; };
|
||||
DA0CC58A1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPSiteEntity+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPSiteEntity+CoreDataProperties.m"; sourceTree = "<group>"; };
|
||||
DA24EBB219DAD4D000FF010B /* Icon-60.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60.png"; sourceTree = "<group>"; };
|
||||
DA24EBB319DAD4D000FF010B /* Icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@2x.png"; sourceTree = "<group>"; };
|
||||
DA24EBB419DAD4D000FF010B /* Icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@3x.png"; sourceTree = "<group>"; };
|
||||
@@ -631,8 +728,8 @@
|
||||
DA24EBCA19DAD4D000FF010B /* launch.sketch */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = launch.sketch; sourceTree = "<group>"; };
|
||||
DA24EBE619DAD6DE00FF010B /* Icon-320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-320.png"; sourceTree = "<group>"; };
|
||||
DA24EBE719DAD6DE00FF010B /* Icon-64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-64.png"; sourceTree = "<group>"; };
|
||||
DA250A13195665A100AC23F1 /* UITableView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+PearlReloadFromArray.m"; sourceTree = "<group>"; };
|
||||
DA250A14195665A100AC23F1 /* UITableView+PearlReloadFromArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+PearlReloadFromArray.h"; sourceTree = "<group>"; };
|
||||
DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITableView+PearlReloadItems.m"; sourceTree = "<group>"; };
|
||||
DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITableView+PearlReloadItems.h"; sourceTree = "<group>"; };
|
||||
DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionReusableView+PearlDequeue.m"; sourceTree = "<group>"; };
|
||||
DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionReusableView+PearlDequeue.h"; sourceTree = "<group>"; };
|
||||
DA25C6B31980D3C10046CDCF /* openssl */ = {isa = PBXFileReference; lastKnownFileType = folder; path = openssl; sourceTree = "<group>"; };
|
||||
@@ -653,14 +750,6 @@
|
||||
DA30E9D315722EF400A68B4C /* Pearl-UIKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-UIKit.m"; sourceTree = "<group>"; };
|
||||
DA30E9D515723E6900A68B4C /* PearlLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlLazy.h; sourceTree = "<group>"; };
|
||||
DA30E9D615723E6900A68B4C /* PearlLazy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlLazy.m; sourceTree = "<group>"; };
|
||||
DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
|
||||
DA32CFE719CF1C8F004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
|
||||
DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPStoredSiteEntity.m; sourceTree = "<group>"; };
|
||||
DA32CFE919CF1C8F004F3F0E /* MPStoredSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPStoredSiteEntity.h; sourceTree = "<group>"; };
|
||||
DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteEntity.m; sourceTree = "<group>"; };
|
||||
DA32CFED19CF1C8F004F3F0E /* MPSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteEntity.h; sourceTree = "<group>"; };
|
||||
DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPGeneratedSiteEntity.m; sourceTree = "<group>"; };
|
||||
DA32CFEF19CF1C8F004F3F0E /* MPGeneratedSiteEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPGeneratedSiteEntity.h; sourceTree = "<group>"; };
|
||||
DA32D01819D046E1004F3F0E /* PearlFixedTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlFixedTableView.m; sourceTree = "<group>"; };
|
||||
DA32D01919D046E1004F3F0E /* PearlFixedTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlFixedTableView.h; sourceTree = "<group>"; };
|
||||
DA32D02019D111C6004F3F0E /* libKCOrderedAccessorFix.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libKCOrderedAccessorFix.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -675,8 +764,6 @@
|
||||
DA32D04B19D2F59B004F3F0E /* meter_fuel@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "meter_fuel@3x.png"; sourceTree = "<group>"; };
|
||||
DA32D04C19D2F59B004F3F0E /* meter_fuel@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "meter_fuel@2x.png"; sourceTree = "<group>"; };
|
||||
DA32D04D19D2F59B004F3F0E /* meter_fuel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = meter_fuel.png; sourceTree = "<group>"; };
|
||||
DA32D05319D741DC004F3F0E /* MPSiteQuestionEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSiteQuestionEntity.h; sourceTree = "<group>"; };
|
||||
DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = "<group>"; };
|
||||
DA32D07719D7D784004F3F0E /* background@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "background@3x.png"; path = "ios/launch/background@3x.png"; sourceTree = "<group>"; };
|
||||
DA32D07819D7D784004F3F0E /* background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "background@2x.png"; path = "ios/launch/background@2x.png"; sourceTree = "<group>"; };
|
||||
DA32D07919D7D784004F3F0E /* background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = background.png; path = ios/launch/background.png; sourceTree = "<group>"; };
|
||||
@@ -713,14 +800,6 @@
|
||||
DA95B50B1C476B6A0067F5EF /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; };
|
||||
DA95B50D1C4776F00067F5EF /* NSMutableSet+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableSet+Pearl.h"; sourceTree = "<group>"; };
|
||||
DA95B50E1C4776F00067F5EF /* NSMutableSet+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableSet+Pearl.m"; sourceTree = "<group>"; };
|
||||
DA95B5111C477DB50067F5EF /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5121C477DB50067F5EF /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5131C477DB50067F5EF /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5141C477DB50067F5EF /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5151C477DB50067F5EF /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5161C477DB50067F5EF /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5171C477DB50067F5EF /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5181C477DB50067F5EF /* MasterPassword 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95D5F014DF0B1E008D1B94 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
|
||||
DAA141191922FED80032B392 /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
|
||||
DAA1411C1922FF020032B392 /* PearlTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlTween.m; sourceTree = "<group>"; };
|
||||
@@ -1434,7 +1513,6 @@
|
||||
DAC632871486D95D0075AEA5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
|
||||
DAC77CAD148291A600BCF976 /* libPearl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPearl.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DAC77CB1148291A600BCF976 /* Pearl-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Pearl-Prefix.pch"; path = "../../Source/Pearl/Pearl-Prefix.pch"; sourceTree = "<group>"; };
|
||||
DACA269A1705DF81002C6C22 /* Crashlytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Crashlytics.plist; sourceTree = "<group>"; };
|
||||
DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = "<group>"; };
|
||||
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
|
||||
DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
|
||||
@@ -1464,35 +1542,6 @@
|
||||
DAF4EF4E190A81E400023C90 /* NSManagedObject+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+Pearl.m"; sourceTree = "<group>"; };
|
||||
DAF4EF4F190A81E400023C90 /* NSManagedObject+Pearl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+Pearl.h"; sourceTree = "<group>"; };
|
||||
DAFC5655172C573B00CB5CC5 /* libInAppSettingsKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libInAppSettingsKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DAFC5664172C57EC00CB5CC5 /* IASKAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKAppSettingsViewController.h; sourceTree = "<group>"; };
|
||||
DAFC5665172C57EC00CB5CC5 /* IASKAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKAppSettingsViewController.m; sourceTree = "<group>"; };
|
||||
DAFC5666172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKAppSettingsWebViewController.h; sourceTree = "<group>"; };
|
||||
DAFC5667172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKAppSettingsWebViewController.m; sourceTree = "<group>"; };
|
||||
DAFC5668172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSpecifierValuesViewController.h; sourceTree = "<group>"; };
|
||||
DAFC5669172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSpecifierValuesViewController.m; sourceTree = "<group>"; };
|
||||
DAFC566A172C57EC00CB5CC5 /* IASKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKViewController.h; sourceTree = "<group>"; };
|
||||
DAFC566C172C57EC00CB5CC5 /* IASKSettingsReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsReader.h; sourceTree = "<group>"; };
|
||||
DAFC566D172C57EC00CB5CC5 /* IASKSettingsReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsReader.m; sourceTree = "<group>"; };
|
||||
DAFC566E172C57EC00CB5CC5 /* IASKSettingsStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStore.h; sourceTree = "<group>"; };
|
||||
DAFC566F172C57EC00CB5CC5 /* IASKSettingsStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStore.m; sourceTree = "<group>"; };
|
||||
DAFC5670172C57EC00CB5CC5 /* IASKSettingsStoreFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStoreFile.h; sourceTree = "<group>"; };
|
||||
DAFC5671172C57EC00CB5CC5 /* IASKSettingsStoreFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStoreFile.m; sourceTree = "<group>"; };
|
||||
DAFC5672172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSettingsStoreUserDefaults.h; sourceTree = "<group>"; };
|
||||
DAFC5673172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSettingsStoreUserDefaults.m; sourceTree = "<group>"; };
|
||||
DAFC5674172C57EC00CB5CC5 /* IASKSpecifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSpecifier.h; sourceTree = "<group>"; };
|
||||
DAFC5675172C57EC00CB5CC5 /* IASKSpecifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSpecifier.m; sourceTree = "<group>"; };
|
||||
DAFC5677172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSSliderSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DAFC5678172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSSliderSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DAFC5679172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSTextFieldSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DAFC567A172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSTextFieldSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DAFC567B172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKPSTitleValueSpecifierViewCell.h; sourceTree = "<group>"; };
|
||||
DAFC567C172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKPSTitleValueSpecifierViewCell.m; sourceTree = "<group>"; };
|
||||
DAFC567D172C57EC00CB5CC5 /* IASKSlider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSlider.h; sourceTree = "<group>"; };
|
||||
DAFC567E172C57EC00CB5CC5 /* IASKSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSlider.m; sourceTree = "<group>"; };
|
||||
DAFC567F172C57EC00CB5CC5 /* IASKSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKSwitch.h; sourceTree = "<group>"; };
|
||||
DAFC5680172C57EC00CB5CC5 /* IASKSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKSwitch.m; sourceTree = "<group>"; };
|
||||
DAFC5681172C57EC00CB5CC5 /* IASKTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IASKTextField.h; sourceTree = "<group>"; };
|
||||
DAFC5682172C57EC00CB5CC5 /* IASKTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IASKTextField.m; sourceTree = "<group>"; };
|
||||
DAFE45D815039823003ABA7C /* NSObject+PearlExport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+PearlExport.h"; sourceTree = "<group>"; };
|
||||
DAFE45D915039823003ABA7C /* NSObject+PearlExport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+PearlExport.m"; sourceTree = "<group>"; };
|
||||
DAFE45DA15039823003ABA7C /* NSString+PearlNSArrayFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+PearlNSArrayFormat.h"; sourceTree = "<group>"; };
|
||||
@@ -1590,6 +1639,7 @@
|
||||
DA2C3D651BD9612F001137B3 /* libz.tbd in Frameworks */,
|
||||
DA2C3D631BD96126001137B3 /* libc++.tbd in Frameworks */,
|
||||
DAA1761B19D86D0D0044227B /* libAttributedMarkdown.a in Frameworks */,
|
||||
DA0CC5421EB57BD4009A8ED9 /* Crashlytics.framework in Frameworks */,
|
||||
DA32D03E19D11293004F3F0E /* libKCOrderedAccessorFix.a in Frameworks */,
|
||||
DA04E33E14B1E70400ECA4F3 /* MobileCoreServices.framework in Frameworks */,
|
||||
DAE2725A19C93B8E007C5262 /* StoreKit.framework in Frameworks */,
|
||||
@@ -1601,15 +1651,13 @@
|
||||
DA672D3014F9413D004A189C /* libPearl.a in Frameworks */,
|
||||
DAEBC45314F6364500987BF6 /* QuartzCore.framework in Frameworks */,
|
||||
DA95D5F214DF0B2C008D1B94 /* MessageUI.framework in Frameworks */,
|
||||
DA48856019A5A82E000C2D79 /* Crashlytics.framework in Frameworks */,
|
||||
DAC632891486D9690075AEA5 /* Security.framework in Frameworks */,
|
||||
DA5BFA49147E415C00F98B1E /* UIKit.framework in Frameworks */,
|
||||
DA0979171E9A81EE00F0BFE8 /* libsodium.a in Frameworks */,
|
||||
DA5BFA4B147E415C00F98B1E /* Foundation.framework in Frameworks */,
|
||||
DA5BFA4D147E415C00F98B1E /* CoreGraphics.framework in Frameworks */,
|
||||
DA5BFA4F147E415C00F98B1E /* CoreData.framework in Frameworks */,
|
||||
93D399433EA75E50656040CB /* Twitter.framework in Frameworks */,
|
||||
DA2C3D611BD95EEE001137B3 /* Fabric.framework in Frameworks */,
|
||||
DA0CC5411EB57BD4009A8ED9 /* Fabric.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1766,6 +1814,76 @@
|
||||
path = lib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC4F41EAB99BA009A8ED9 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC4F51EAB99BA009A8ED9 /* IASKLocalizable.strings */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC5051EAB99BA009A8ED9 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC5061EAB99BA009A8ED9 /* IASKAppSettingsViewController.h */,
|
||||
DA0CC5071EAB99BA009A8ED9 /* IASKAppSettingsViewController.m */,
|
||||
DA0CC5081EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.h */,
|
||||
DA0CC5091EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m */,
|
||||
DA0CC50A1EAB99BA009A8ED9 /* IASKMultipleValueSelection.h */,
|
||||
DA0CC50B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m */,
|
||||
DA0CC50C1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.h */,
|
||||
DA0CC50D1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m */,
|
||||
DA0CC50E1EAB99BA009A8ED9 /* IASKViewController.h */,
|
||||
);
|
||||
path = Controllers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC50F1EAB99BA009A8ED9 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC5101EAB99BA009A8ED9 /* IASKSettingsReader.h */,
|
||||
DA0CC5111EAB99BA009A8ED9 /* IASKSettingsReader.m */,
|
||||
DA0CC5121EAB99BA009A8ED9 /* IASKSettingsStore.h */,
|
||||
DA0CC5131EAB99BA009A8ED9 /* IASKSettingsStore.m */,
|
||||
DA0CC5141EAB99BA009A8ED9 /* IASKSettingsStoreFile.h */,
|
||||
DA0CC5151EAB99BA009A8ED9 /* IASKSettingsStoreFile.m */,
|
||||
DA0CC5161EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.h */,
|
||||
DA0CC5171EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m */,
|
||||
DA0CC5181EAB99BA009A8ED9 /* IASKSpecifier.h */,
|
||||
DA0CC5191EAB99BA009A8ED9 /* IASKSpecifier.m */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC51A1EAB99BA009A8ED9 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC51B1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.h */,
|
||||
DA0CC51C1EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m */,
|
||||
DA0CC51D1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.h */,
|
||||
DA0CC51E1EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m */,
|
||||
DA0CC51F1EAB99BA009A8ED9 /* IASKSlider.h */,
|
||||
DA0CC5201EAB99BA009A8ED9 /* IASKSlider.m */,
|
||||
DA0CC5211EAB99BA009A8ED9 /* IASKSwitch.h */,
|
||||
DA0CC5221EAB99BA009A8ED9 /* IASKSwitch.m */,
|
||||
DA0CC5231EAB99BA009A8ED9 /* IASKTextField.h */,
|
||||
DA0CC5241EAB99BA009A8ED9 /* IASKTextField.m */,
|
||||
DA0CC5251EAB99BA009A8ED9 /* IASKTextView.h */,
|
||||
DA0CC5261EAB99BA009A8ED9 /* IASKTextView.m */,
|
||||
DA0CC5271EAB99BA009A8ED9 /* IASKTextViewCell.h */,
|
||||
DA0CC5281EAB99BA009A8ED9 /* IASKTextViewCell.m */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA0CC5391EB57B5C009A8ED9 /* Fabric */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC53A1EB57B5C009A8ED9 /* Fabric.plist */,
|
||||
);
|
||||
path = Fabric;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA24EBB019DAD4D000FF010B /* ios */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1927,6 +2045,7 @@
|
||||
DAA141181922FED80032B392 /* iOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC53F1EB57B91009A8ED9 /* Fabric.framework */,
|
||||
DAA141191922FED80032B392 /* Crashlytics.framework */,
|
||||
);
|
||||
path = iOS;
|
||||
@@ -2676,7 +2795,7 @@
|
||||
children = (
|
||||
DABD3BD71711E2DC00CF925C /* iOS */,
|
||||
DA771FE41E6E1595004D7EDE /* MasterPassword-Prefix.pch */,
|
||||
DA95B5101C477DB50067F5EF /* MasterPassword.xcdatamodeld */,
|
||||
DA0CC5441EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld */,
|
||||
DABD3BA01711E2DC00CF925C /* MPAlgorithm.h */,
|
||||
DABD3BA11711E2DC00CF925C /* MPAlgorithm.m */,
|
||||
DABD3BA21711E2DC00CF925C /* MPAlgorithmV0.h */,
|
||||
@@ -2701,20 +2820,30 @@
|
||||
DABD3BB51711E2DC00CF925C /* MPEntities.m */,
|
||||
93D399F244BB522A317811BB /* MPFixable.h */,
|
||||
93D39A813CA9D7E192261ED2 /* MPFixable.m */,
|
||||
DA32CFEF19CF1C8F004F3F0E /* MPGeneratedSiteEntity.h */,
|
||||
DA32CFEE19CF1C8F004F3F0E /* MPGeneratedSiteEntity.m */,
|
||||
DA0CC5781EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.h */,
|
||||
DA0CC5791EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m */,
|
||||
DA0CC57A1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.h */,
|
||||
DA0CC57B1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m */,
|
||||
DABD3BB61711E2DC00CF925C /* MPKey.h */,
|
||||
DABD3BB71711E2DC00CF925C /* MPKey.m */,
|
||||
DA32CFED19CF1C8F004F3F0E /* MPSiteEntity.h */,
|
||||
DA32CFEC19CF1C8F004F3F0E /* MPSiteEntity.m */,
|
||||
DA32D05319D741DC004F3F0E /* MPSiteQuestionEntity.h */,
|
||||
DA32D05419D741DC004F3F0E /* MPSiteQuestionEntity.m */,
|
||||
DA32CFE919CF1C8F004F3F0E /* MPStoredSiteEntity.h */,
|
||||
DA32CFE819CF1C8F004F3F0E /* MPStoredSiteEntity.m */,
|
||||
DA0CC5881EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.h */,
|
||||
DA0CC5891EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m */,
|
||||
DA0CC58A1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.h */,
|
||||
DA0CC58B1EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m */,
|
||||
DA0CC57C1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.h */,
|
||||
DA0CC57D1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m */,
|
||||
DA0CC57E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.h */,
|
||||
DA0CC57F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m */,
|
||||
DA0CC5801EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.h */,
|
||||
DA0CC5811EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m */,
|
||||
DA0CC5821EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.h */,
|
||||
DA0CC5831EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m */,
|
||||
DABD3BB81711E2DC00CF925C /* MPTypes.h */,
|
||||
93D39D72239990DDAC2D75B0 /* MPTypes.m */,
|
||||
DA32CFE719CF1C8F004F3F0E /* MPUserEntity.h */,
|
||||
DA32CFE619CF1C8F004F3F0E /* MPUserEntity.m */,
|
||||
DA0CC5841EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.h */,
|
||||
DA0CC5851EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m */,
|
||||
DA0CC5861EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.h */,
|
||||
DA0CC5871EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m */,
|
||||
93D393CB0B1F4EC8C17CFE43 /* NSString+MPMarkDown.h */,
|
||||
93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */,
|
||||
);
|
||||
@@ -2813,7 +2942,7 @@
|
||||
DACA23B41705DF7D002C6C22 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DACA26991705DF81002C6C22 /* Crashlytics */,
|
||||
DA0CC5391EB57B5C009A8ED9 /* Fabric */,
|
||||
DACA29701705E1A8002C6C22 /* Data */,
|
||||
DAE1EF2417E942DE00BC0086 /* Localizable.strings */,
|
||||
DABD360D1711E29400CF925C /* Media */,
|
||||
@@ -2821,14 +2950,6 @@
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DACA26991705DF81002C6C22 /* Crashlytics */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DACA269A1705DF81002C6C22 /* Crashlytics.plist */,
|
||||
);
|
||||
path = Crashlytics;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DACA29701705E1A8002C6C22 /* Data */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2861,64 +2982,15 @@
|
||||
DAFC5662172C57EC00CB5CC5 /* InAppSettingsKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAFC5663172C57EC00CB5CC5 /* Controllers */,
|
||||
DAFC566B172C57EC00CB5CC5 /* Models */,
|
||||
DAFC5676172C57EC00CB5CC5 /* Views */,
|
||||
DA0CC4F41EAB99BA009A8ED9 /* Resources */,
|
||||
DA0CC5051EAB99BA009A8ED9 /* Controllers */,
|
||||
DA0CC50F1EAB99BA009A8ED9 /* Models */,
|
||||
DA0CC51A1EAB99BA009A8ED9 /* Views */,
|
||||
);
|
||||
name = InAppSettingsKit;
|
||||
path = InAppSettingsKit/InAppSettingsKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAFC5663172C57EC00CB5CC5 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAFC5664172C57EC00CB5CC5 /* IASKAppSettingsViewController.h */,
|
||||
DAFC5665172C57EC00CB5CC5 /* IASKAppSettingsViewController.m */,
|
||||
DAFC5666172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.h */,
|
||||
DAFC5667172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m */,
|
||||
DAFC5668172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.h */,
|
||||
DAFC5669172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m */,
|
||||
DAFC566A172C57EC00CB5CC5 /* IASKViewController.h */,
|
||||
);
|
||||
path = Controllers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAFC566B172C57EC00CB5CC5 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAFC566C172C57EC00CB5CC5 /* IASKSettingsReader.h */,
|
||||
DAFC566D172C57EC00CB5CC5 /* IASKSettingsReader.m */,
|
||||
DAFC566E172C57EC00CB5CC5 /* IASKSettingsStore.h */,
|
||||
DAFC566F172C57EC00CB5CC5 /* IASKSettingsStore.m */,
|
||||
DAFC5670172C57EC00CB5CC5 /* IASKSettingsStoreFile.h */,
|
||||
DAFC5671172C57EC00CB5CC5 /* IASKSettingsStoreFile.m */,
|
||||
DAFC5672172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.h */,
|
||||
DAFC5673172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m */,
|
||||
DAFC5674172C57EC00CB5CC5 /* IASKSpecifier.h */,
|
||||
DAFC5675172C57EC00CB5CC5 /* IASKSpecifier.m */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAFC5676172C57EC00CB5CC5 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAFC5677172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.h */,
|
||||
DAFC5678172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m */,
|
||||
DAFC5679172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.h */,
|
||||
DAFC567A172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m */,
|
||||
DAFC567B172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.h */,
|
||||
DAFC567C172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m */,
|
||||
DAFC567D172C57EC00CB5CC5 /* IASKSlider.h */,
|
||||
DAFC567E172C57EC00CB5CC5 /* IASKSlider.m */,
|
||||
DAFC567F172C57EC00CB5CC5 /* IASKSwitch.h */,
|
||||
DAFC5680172C57EC00CB5CC5 /* IASKSwitch.m */,
|
||||
DAFC5681172C57EC00CB5CC5 /* IASKTextField.h */,
|
||||
DAFC5682172C57EC00CB5CC5 /* IASKTextField.m */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DAFE45D715039823003ABA7C /* Pearl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2983,6 +3055,8 @@
|
||||
DAA1411C1922FF020032B392 /* PearlTween.m */,
|
||||
DAFE45F815039823003ABA7C /* README */,
|
||||
DAFE45F915039823003ABA7C /* Resources */,
|
||||
93D39FBF8FCEB4C106272334 /* NSOrderedSetOrArray.h */,
|
||||
93D39789AAF49338F8AC8B02 /* NSOrderedSetOrArray.m */,
|
||||
);
|
||||
path = Pearl;
|
||||
sourceTree = "<group>";
|
||||
@@ -3066,8 +3140,8 @@
|
||||
DAFE462515039823003ABA7C /* Resources */,
|
||||
DA250A16195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.h */,
|
||||
DA250A15195665A100AC23F1 /* UICollectionReusableView+PearlDequeue.m */,
|
||||
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */,
|
||||
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */,
|
||||
93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadItems.h */,
|
||||
93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadItems.m */,
|
||||
DAFE4A63150399FF003ABA83 /* UIControl+PearlBlocks.h */,
|
||||
DAFE4A63150399FF003ABA81 /* UIControl+PearlBlocks.m */,
|
||||
DAFE4A63150399FF003ABA8B /* UIControl+PearlSelect.h */,
|
||||
@@ -3080,8 +3154,8 @@
|
||||
93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */,
|
||||
DAFE4A63150399FF003ABA8F /* UIScrollView+PearlFlashingIndicators.h */,
|
||||
DAFE4A63150399FF003ABA8D /* UIScrollView+PearlFlashingIndicators.m */,
|
||||
DA250A14195665A100AC23F1 /* UITableView+PearlReloadFromArray.h */,
|
||||
DA250A13195665A100AC23F1 /* UITableView+PearlReloadFromArray.m */,
|
||||
DA250A14195665A100AC23F1 /* UITableView+PearlReloadItems.h */,
|
||||
DA250A13195665A100AC23F1 /* UITableView+PearlReloadItems.m */,
|
||||
DAE2726219CE9CB3007C5262 /* UITableViewCell+PearlDeque.h */,
|
||||
DAE2726119CE9CB3007C5262 /* UITableViewCell+PearlDeque.m */,
|
||||
93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */,
|
||||
@@ -3092,6 +3166,10 @@
|
||||
DAEFB01C19BCBD9E00525079 /* UIView+LayoutGone.m */,
|
||||
DAEC85B418E3DD9A007FC0DF /* UIView+Touches.h */,
|
||||
DAEC85B118E3DD9A007FC0DF /* UIView+Touches.m */,
|
||||
93D39488AB33616661725929 /* UIView+AlphaScale.m */,
|
||||
93D392D76C091DEA3319F11D /* UIView+AlphaScale.h */,
|
||||
93D3952910EDB8E0EBC94BA9 /* UIView+Visible.m */,
|
||||
93D39B7B765546B1F1900CB7 /* UIView+Visible.h */,
|
||||
);
|
||||
path = "Pearl-UIKit";
|
||||
sourceTree = "<group>";
|
||||
@@ -3143,7 +3221,7 @@
|
||||
DAFE4A2615039824003ABA7C /* PearlLogger.h in Headers */,
|
||||
DAFE4A2815039824003ABA7C /* PearlMathUtils.h in Headers */,
|
||||
DAFE4A2A15039824003ABA7C /* PearlObjectUtils.h in Headers */,
|
||||
DA250A18195665A100AC23F1 /* UITableView+PearlReloadFromArray.h in Headers */,
|
||||
DA250A18195665A100AC23F1 /* UITableView+PearlReloadItems.h in Headers */,
|
||||
DAFE4A2C15039824003ABA7C /* PearlResettable.h in Headers */,
|
||||
DAFE4A2D15039824003ABA7C /* PearlStrings.h in Headers */,
|
||||
DAFE4A2F15039824003ABA7C /* PearlStringUtils.h in Headers */,
|
||||
@@ -3197,8 +3275,11 @@
|
||||
93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */,
|
||||
93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */,
|
||||
93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */,
|
||||
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */,
|
||||
93D39A53D76CA70786423458 /* UICollectionView+PearlReloadItems.h in Headers */,
|
||||
93D39AA4A0BE66A872CCC02E /* NSPersistentStore+PearlMigration.h in Headers */,
|
||||
93D399E4BC1E092A8C8B12AE /* NSOrderedSetOrArray.h in Headers */,
|
||||
93D3959696396A91961C6148 /* UIView+AlphaScale.h in Headers */,
|
||||
93D39BFB5F5F9337F6565DE3 /* UIView+Visible.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -3232,7 +3313,7 @@
|
||||
DA5BFA41147E415C00F98B1E /* Frameworks */,
|
||||
DA5BFA42147E415C00F98B1E /* Resources */,
|
||||
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */,
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -3468,6 +3549,8 @@
|
||||
zh_HK,
|
||||
zh_TW,
|
||||
zu,
|
||||
Base,
|
||||
"pt-PT",
|
||||
);
|
||||
mainGroup = DA5BFA39147E415C00F98B1E;
|
||||
productRefGroup = DA5BFA45147E415C00F98B1E /* Products */;
|
||||
@@ -3491,7 +3574,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAFE4A5A1503982E003ABA7C /* Pearl.strings in Resources */,
|
||||
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */,
|
||||
DA0CC53B1EB57B5C009A8ED9 /* Fabric.plist in Resources */,
|
||||
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
|
||||
DA32D04F19D2F59B004F3F0E /* meter_fuel@2x.png in Resources */,
|
||||
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
|
||||
@@ -3721,19 +3804,19 @@
|
||||
shellScript = "exec Scripts/genassets";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = {
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script: Crashlytics";
|
||||
name = "Run Script: Fabric";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = "/bin/bash -e";
|
||||
shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Crashlytics/Crashlytics.plist)\n[[ $apiKey ]] && External/iOS/Crashlytics.framework/run \"$apiKey\"";
|
||||
shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Fabric/Fabric.plist)\n[[ $apiKey ]] && External/iOS/Fabric.framework/run \"$apiKey\"";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
@@ -3751,7 +3834,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DA0CC5921EB6B030009A8ED9 /* MPUserEntity+CoreDataClass.m in Sources */,
|
||||
DABD3BFD1711E2DC00CF925C /* MPAlgorithm.m in Sources */,
|
||||
DA0CC5951EB6B030009A8ED9 /* MPSiteEntity+CoreDataProperties.m in Sources */,
|
||||
DABD3BFE1711E2DC00CF925C /* MPAlgorithmV0.m in Sources */,
|
||||
DABD3BFF1711E2DC00CF925C /* MPAlgorithmV1.m in Sources */,
|
||||
DABD3C001711E2DC00CF925C /* MPAppDelegate_Key.m in Sources */,
|
||||
@@ -3759,38 +3844,40 @@
|
||||
DABD3C021711E2DC00CF925C /* MPAppDelegate_Store.m in Sources */,
|
||||
DABD3C031711E2DC00CF925C /* MPConfig.m in Sources */,
|
||||
DABD3C071711E2DC00CF925C /* MPEntities.m in Sources */,
|
||||
DA32CFF319CF1C8F004F3F0E /* MPSiteEntity.m in Sources */,
|
||||
DABD3C081711E2DC00CF925C /* MPKey.m in Sources */,
|
||||
DABD3C151711E2DC00CF925C /* MPiOSAppDelegate.m in Sources */,
|
||||
DA0CC5941EB6B030009A8ED9 /* MPSiteEntity+CoreDataClass.m in Sources */,
|
||||
DABD3C1C1711E2DC00CF925C /* MPGuideViewController.m in Sources */,
|
||||
DABD3C1E1711E2DC00CF925C /* MPPreferencesViewController.m in Sources */,
|
||||
DABD3C1F1711E2DC00CF925C /* MPTypeViewController.m in Sources */,
|
||||
DABD3C211711E2DC00CF925C /* MPiOSConfig.m in Sources */,
|
||||
DA0CC5901EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataClass.m in Sources */,
|
||||
DA0CC58D1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataProperties.m in Sources */,
|
||||
DABD3C271711E2DC00CF925C /* main.m in Sources */,
|
||||
DA0CC5931EB6B030009A8ED9 /* MPUserEntity+CoreDataProperties.m in Sources */,
|
||||
93D39F8A9254177891F38705 /* MPSetupViewController.m in Sources */,
|
||||
DA095E75172F4CD8001C948B /* MPLogsViewController.m in Sources */,
|
||||
93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */,
|
||||
DA32CFF419CF1C8F004F3F0E /* MPGeneratedSiteEntity.m in Sources */,
|
||||
93D3957237D303DE2D38C267 /* MPAvatarCell.m in Sources */,
|
||||
93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */,
|
||||
DA32D05519D741DC004F3F0E /* MPSiteQuestionEntity.m in Sources */,
|
||||
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */,
|
||||
93D39392DEDA376F93C6C718 /* MPCell.m in Sources */,
|
||||
DA0CC58E1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataClass.m in Sources */,
|
||||
93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */,
|
||||
93D398ECD7D1A0DEDDADF516 /* MPEmergencyViewController.m in Sources */,
|
||||
DA95B50F1C4776F00067F5EF /* NSMutableSet+Pearl.m in Sources */,
|
||||
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */,
|
||||
DA0CC5911EB6B030009A8ED9 /* MPStoredSiteEntity+CoreDataProperties.m in Sources */,
|
||||
93D39673DDC085BE72C34D7C /* MPPopdownSegue.m in Sources */,
|
||||
93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */,
|
||||
DA0CC58F1EB6B030009A8ED9 /* MPSiteQuestionEntity+CoreDataProperties.m in Sources */,
|
||||
93D396D8B67DA6522CDBA142 /* MPCoachmarkViewController.m in Sources */,
|
||||
DAADBFE01A68763B00F7A756 /* mpw-algorithm.c in Sources */,
|
||||
DA95B5191C477DB50067F5EF /* MasterPassword.xcdatamodeld in Sources */,
|
||||
DA0CC54E1EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld in Sources */,
|
||||
93D39EAA4D064193074D3021 /* MPFixable.m in Sources */,
|
||||
DA32CFF119CF1C8F004F3F0E /* MPStoredSiteEntity.m in Sources */,
|
||||
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */,
|
||||
93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */,
|
||||
93D3939661CE37180AF7CD6A /* MPStoreViewController.m in Sources */,
|
||||
DA32CFF019CF1C8F004F3F0E /* MPUserEntity.m in Sources */,
|
||||
93D390C1B93F9D3AE37DD0A5 /* MPAnswersViewController.m in Sources */,
|
||||
93D399D7E08A142776A74CB8 /* MPOverlayViewController.m in Sources */,
|
||||
93D39A27F2506C6FEEF9C588 /* MPAlgorithmV2.m in Sources */,
|
||||
@@ -3800,6 +3887,7 @@
|
||||
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
|
||||
93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */,
|
||||
93D39E5F7F6D7F5C0FAD090F /* MPTypes.m in Sources */,
|
||||
DA0CC58C1EB6B030009A8ED9 /* MPGeneratedSiteEntity+CoreDataClass.m in Sources */,
|
||||
DA92614E1BE1A57500369DE5 /* MPAppDelegate_InApp.m in Sources */,
|
||||
93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */,
|
||||
);
|
||||
@@ -3853,7 +3941,7 @@
|
||||
DAFE4A3515039824003ABA7C /* PearlCryptUtils.m in Sources */,
|
||||
DAFE4A3715039824003ABA7C /* PearlKeyChain.m in Sources */,
|
||||
DA72BD7B19C1510C00E6ACFE /* UIView+FontScale.m in Sources */,
|
||||
DA250A17195665A100AC23F1 /* UITableView+PearlReloadFromArray.m in Sources */,
|
||||
DA250A17195665A100AC23F1 /* UITableView+PearlReloadItems.m in Sources */,
|
||||
DAFE4A3915039824003ABA7C /* PearlRSAKey.m in Sources */,
|
||||
DAFE4A3F15039824003ABA7C /* PearlAlert.m in Sources */,
|
||||
DAFE4A4115039824003ABA7C /* PearlArrayTVC.m in Sources */,
|
||||
@@ -3902,8 +3990,11 @@
|
||||
DAA141201922FF020032B392 /* PearlTween.m in Sources */,
|
||||
93D391ECBD9BD2C64115B5DD /* PearlSizedTextView.m in Sources */,
|
||||
93D39E34FD28D24FE3442C48 /* UITextView+PearlAttributes.m in Sources */,
|
||||
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */,
|
||||
93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadItems.m in Sources */,
|
||||
93D3928D629EA563F9EC4909 /* NSPersistentStore+PearlMigration.m in Sources */,
|
||||
93D392A33CCE85431E910C7B /* NSOrderedSetOrArray.m in Sources */,
|
||||
93D393AA69A1193401160418 /* UIView+AlphaScale.m in Sources */,
|
||||
93D3942C1B117EE4851AA7B6 /* UIView+Visible.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -3911,26 +4002,50 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DAFC5683172C57EC00CB5CC5 /* IASKAppSettingsViewController.m in Sources */,
|
||||
DAFC5684172C57EC00CB5CC5 /* IASKAppSettingsWebViewController.m in Sources */,
|
||||
DAFC5685172C57EC00CB5CC5 /* IASKSpecifierValuesViewController.m in Sources */,
|
||||
DAFC5686172C57EC00CB5CC5 /* IASKSettingsReader.m in Sources */,
|
||||
DAFC5687172C57EC00CB5CC5 /* IASKSettingsStore.m in Sources */,
|
||||
DAFC5688172C57EC00CB5CC5 /* IASKSettingsStoreFile.m in Sources */,
|
||||
DAFC5689172C57EC00CB5CC5 /* IASKSettingsStoreUserDefaults.m in Sources */,
|
||||
DAFC568A172C57EC00CB5CC5 /* IASKSpecifier.m in Sources */,
|
||||
DAFC568B172C57EC00CB5CC5 /* IASKPSSliderSpecifierViewCell.m in Sources */,
|
||||
DAFC568C172C57EC00CB5CC5 /* IASKPSTextFieldSpecifierViewCell.m in Sources */,
|
||||
DAFC568D172C57EC00CB5CC5 /* IASKPSTitleValueSpecifierViewCell.m in Sources */,
|
||||
DAFC568E172C57EC00CB5CC5 /* IASKSlider.m in Sources */,
|
||||
DAFC568F172C57EC00CB5CC5 /* IASKSwitch.m in Sources */,
|
||||
DAFC5690172C57EC00CB5CC5 /* IASKTextField.m in Sources */,
|
||||
DA0CC52B1EAB99BA009A8ED9 /* IASKMultipleValueSelection.m in Sources */,
|
||||
DA0CC5321EAB99BA009A8ED9 /* IASKPSSliderSpecifierViewCell.m in Sources */,
|
||||
DA0CC52D1EAB99BA009A8ED9 /* IASKSettingsReader.m in Sources */,
|
||||
DA0CC52A1EAB99BA009A8ED9 /* IASKAppSettingsWebViewController.m in Sources */,
|
||||
DA0CC5291EAB99BA009A8ED9 /* IASKAppSettingsViewController.m in Sources */,
|
||||
DA0CC5381EAB99BA009A8ED9 /* IASKTextViewCell.m in Sources */,
|
||||
DA0CC5311EAB99BA009A8ED9 /* IASKSpecifier.m in Sources */,
|
||||
DA0CC5341EAB99BA009A8ED9 /* IASKSlider.m in Sources */,
|
||||
DA0CC5351EAB99BA009A8ED9 /* IASKSwitch.m in Sources */,
|
||||
DA0CC5331EAB99BA009A8ED9 /* IASKPSTextFieldSpecifierViewCell.m in Sources */,
|
||||
DA0CC52E1EAB99BA009A8ED9 /* IASKSettingsStore.m in Sources */,
|
||||
DA0CC52C1EAB99BA009A8ED9 /* IASKSpecifierValuesViewController.m in Sources */,
|
||||
DA0CC5361EAB99BA009A8ED9 /* IASKTextField.m in Sources */,
|
||||
DA0CC5371EAB99BA009A8ED9 /* IASKTextView.m in Sources */,
|
||||
DA0CC52F1EAB99BA009A8ED9 /* IASKSettingsStoreFile.m in Sources */,
|
||||
DA0CC5301EAB99BA009A8ED9 /* IASKSettingsStoreUserDefaults.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
DA0CC4F51EAB99BA009A8ED9 /* IASKLocalizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
DA0CC4F61EAB99BA009A8ED9 /* Base */,
|
||||
DA0CC4F71EAB99BA009A8ED9 /* de */,
|
||||
DA0CC4F81EAB99BA009A8ED9 /* el */,
|
||||
DA0CC4F91EAB99BA009A8ED9 /* en */,
|
||||
DA0CC4FA1EAB99BA009A8ED9 /* es */,
|
||||
DA0CC4FB1EAB99BA009A8ED9 /* fr */,
|
||||
DA0CC4FC1EAB99BA009A8ED9 /* it */,
|
||||
DA0CC4FD1EAB99BA009A8ED9 /* ja */,
|
||||
DA0CC4FE1EAB99BA009A8ED9 /* nl */,
|
||||
DA0CC4FF1EAB99BA009A8ED9 /* pt-PT */,
|
||||
DA0CC5001EAB99BA009A8ED9 /* pt */,
|
||||
DA0CC5011EAB99BA009A8ED9 /* ru */,
|
||||
DA0CC5021EAB99BA009A8ED9 /* sv */,
|
||||
DA0CC5031EAB99BA009A8ED9 /* th */,
|
||||
DA0CC5041EAB99BA009A8ED9 /* tr */,
|
||||
);
|
||||
name = IASKLocalizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
@@ -3997,7 +4112,6 @@
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"ADHOC=1",
|
||||
"$(inherited)",
|
||||
"NDEBUG=1",
|
||||
"NS_BLOCK_ASSERTIONS=1",
|
||||
@@ -4054,6 +4168,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
@@ -4063,8 +4178,9 @@
|
||||
"\"$(SRCROOT)/External/iOS\"",
|
||||
);
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
|
||||
@@ -4180,6 +4296,7 @@
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
"CRASHLYTICS=1",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
@@ -4265,7 +4382,6 @@
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"ADHOC=1",
|
||||
"$(inherited)",
|
||||
"NDEBUG=1",
|
||||
"NS_BLOCK_ASSERTIONS=1",
|
||||
@@ -4322,6 +4438,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@@ -4329,8 +4446,9 @@
|
||||
"\"$(SRCROOT)/External/iOS\"",
|
||||
);
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
|
||||
@@ -4348,6 +4466,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/iOS/MasterPassword.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
@@ -4357,8 +4476,9 @@
|
||||
"\"$(SRCROOT)/External/iOS\"",
|
||||
);
|
||||
GCC_PREFIX_HEADER = "Source/MasterPassword-Prefix.pch";
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
|
||||
INFOPLIST_FILE = "Source/iOS/MasterPassword-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/External/libsodium/libsodium-ios/lib",
|
||||
@@ -4545,19 +4665,20 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
DA95B5101C477DB50067F5EF /* MasterPassword.xcdatamodeld */ = {
|
||||
DA0CC5441EB6AD0E009A8ED9 /* MasterPassword.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DA95B5111C477DB50067F5EF /* MasterPassword 1.xcdatamodel */,
|
||||
DA95B5121C477DB50067F5EF /* MasterPassword 2.xcdatamodel */,
|
||||
DA95B5131C477DB50067F5EF /* MasterPassword 3.xcdatamodel */,
|
||||
DA95B5141C477DB50067F5EF /* MasterPassword 4.xcdatamodel */,
|
||||
DA95B5151C477DB50067F5EF /* MasterPassword 5.xcdatamodel */,
|
||||
DA95B5161C477DB50067F5EF /* MasterPassword 6.xcdatamodel */,
|
||||
DA95B5171C477DB50067F5EF /* MasterPassword 7.xcdatamodel */,
|
||||
DA95B5181C477DB50067F5EF /* MasterPassword 8.xcdatamodel */,
|
||||
DA0CC5451EB6AD0E009A8ED9 /* MasterPassword 1.xcdatamodel */,
|
||||
DA0CC5461EB6AD0E009A8ED9 /* MasterPassword 2.xcdatamodel */,
|
||||
DA0CC5471EB6AD0E009A8ED9 /* MasterPassword 3.xcdatamodel */,
|
||||
DA0CC5481EB6AD0E009A8ED9 /* MasterPassword 4.xcdatamodel */,
|
||||
DA0CC5491EB6AD0E009A8ED9 /* MasterPassword 5.xcdatamodel */,
|
||||
DA0CC54A1EB6AD0E009A8ED9 /* MasterPassword 6.xcdatamodel */,
|
||||
DA0CC54B1EB6AD0E009A8ED9 /* MasterPassword 7.xcdatamodel */,
|
||||
DA0CC54C1EB6AD0E009A8ED9 /* MasterPassword 8.xcdatamodel */,
|
||||
DA0CC54D1EB6AD0E009A8ED9 /* MasterPassword 9.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DA95B5181C477DB50067F5EF /* MasterPassword 8.xcdatamodel */;
|
||||
currentVersion = DA0CC54D1EB6AD0E009A8ED9 /* MasterPassword 9.xcdatamodel */;
|
||||
path = MasterPassword.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
||||
@@ -26,8 +26,9 @@
|
||||
DA09745A1E99582900F0BFE8 /* mpw-tests-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974561E99582200F0BFE8 /* mpw-tests-util.c */; };
|
||||
DA09745B1E99582900F0BFE8 /* mpw-tests.c in Sources */ = {isa = PBXBuildFile; fileRef = DA0974571E99582200F0BFE8 /* mpw-tests.c */; };
|
||||
DA09745E1E99586600F0BFE8 /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA09745D1E99586600F0BFE8 /* libxml2.tbd */; };
|
||||
DA09795D1E9A824700F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; };
|
||||
DA0979681E9A834C00F0BFE8 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; };
|
||||
DA0CC53E1EB57B69009A8ED9 /* Fabric.plist in Resources */ = {isa = PBXBuildFile; fileRef = DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */; };
|
||||
DA0CC5591EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA0CC54F1EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld */; };
|
||||
DA1000801998A4C6002B873F /* openssl in Headers */ = {isa = PBXBuildFile; fileRef = DAE8E65719867AF500416A0F /* openssl */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DA16B341170661DB000A0EAB /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA16B340170661DB000A0EAB /* Carbon.framework */; };
|
||||
DA16B342170661E0000A0EAB /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC632871486D95D0075AEA5 /* Security.framework */; };
|
||||
@@ -97,7 +98,6 @@
|
||||
DA9261521BE1A86700369DE5 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261501BE1A86700369DE5 /* Fabric.framework */; };
|
||||
DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261531BE1A88900369DE5 /* libc++.tbd */; };
|
||||
DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = DA9261551BE1A89600369DE5 /* libz.tbd */; };
|
||||
DA95B5231C477DE10067F5EF /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA95B51A1C477DE10067F5EF /* MasterPassword.xcdatamodeld */; };
|
||||
DAAA81B0195A8D1300FA30D9 /* gradient.png in Resources */ = {isa = PBXBuildFile; fileRef = DAAA81AF195A8D1300FA30D9 /* gradient.png */; };
|
||||
DAADCC4719FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h in Headers */ = {isa = PBXBuildFile; fileRef = DAADCC3E19FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h */; };
|
||||
DAADCC4819FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */; };
|
||||
@@ -149,7 +149,6 @@
|
||||
DACA27381705DF81002C6C22 /* menu-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = DACA24581705DF7D002C6C22 /* menu-icon.png */; };
|
||||
DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */; };
|
||||
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */; };
|
||||
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA269A1705DF81002C6C22 /* Crashlytics.plist */; };
|
||||
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */ = {isa = PBXBuildFile; fileRef = DACA29711705E1A8002C6C22 /* ciphers.plist */; };
|
||||
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */ = {isa = PBXBuildFile; fileRef = DACA29721705E1A8002C6C22 /* dictionary.lst */; };
|
||||
DACA298D1705E2BD002C6C22 /* JRSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA29771705E2BD002C6C22 /* JRSwizzle.h */; };
|
||||
@@ -157,6 +156,7 @@
|
||||
DACBFCDF1C59B22E007EF90F /* NSMutableSet+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DACBFCDC1C59B22E007EF90F /* NSMutableSet+Pearl.m */; };
|
||||
DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD9B5EF1762CAA4001835F9 /* ServiceManagement.framework */; };
|
||||
DAD9B5F11762CAB9001835F9 /* MasterPassword-Mac-LoginHelper.app in Copy LoginHelper */ = {isa = PBXBuildFile; fileRef = DAD9B5E6176299BA001835F9 /* MasterPassword-Mac-LoginHelper.app */; };
|
||||
DADD5DFA1EA173B0005E7D96 /* libsodium.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DA0979571E9A824700F0BFE8 /* libsodium.a */; };
|
||||
DAEB942E18B47FB3000490CC /* MPInitialWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */; };
|
||||
DAF4EF56190A828100023C90 /* Exo2.0-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = DAF4EF52190A828100023C90 /* Exo2.0-Thin.otf */; };
|
||||
DAF4EF57190A828100023C90 /* Exo2.0-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = DAF4EF53190A828100023C90 /* Exo2.0-Regular.otf */; };
|
||||
@@ -291,7 +291,6 @@
|
||||
DA09745C1E99583B00F0BFE8 /* mpw-tests-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "mpw-tests-util.h"; path = "../../platform-independent/cli-c/cli/mpw-tests-util.h"; sourceTree = "<group>"; };
|
||||
DA09745D1E99586600F0BFE8 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; };
|
||||
DA09745F1E995EB500F0BFE8 /* mpw_tests.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = mpw_tests.xml; path = ../../core/java/tests/src/main/resources/mpw_tests.xml; sourceTree = "<group>"; };
|
||||
DA0974611E9961F100F0BFE8 /* libsodium.18.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsodium.18.dylib; path = ../../../../../../../usr/local/Cellar/libsodium/1.0.12/lib/libsodium.18.dylib; sourceTree = "<group>"; };
|
||||
DA09791B1E9A824700F0BFE8 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = "<group>"; };
|
||||
DA09791C1E9A824700F0BFE8 /* crypto_aead_aes256gcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypto_aead_aes256gcm.h; sourceTree = "<group>"; };
|
||||
DA09791D1E9A824700F0BFE8 /* crypto_aead_chacha20poly1305.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypto_aead_chacha20poly1305.h; sourceTree = "<group>"; };
|
||||
@@ -350,11 +349,17 @@
|
||||
DA0979521E9A824700F0BFE8 /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = "<group>"; };
|
||||
DA0979531E9A824700F0BFE8 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
|
||||
DA0979541E9A824700F0BFE8 /* sodium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sodium.h; sourceTree = "<group>"; };
|
||||
DA0979561E9A824700F0BFE8 /* libsodium.18.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libsodium.18.dylib; sourceTree = "<group>"; };
|
||||
DA0979571E9A824700F0BFE8 /* libsodium.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsodium.a; sourceTree = "<group>"; };
|
||||
DA0979581E9A824700F0BFE8 /* libsodium.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libsodium.dylib; sourceTree = "<group>"; };
|
||||
DA0979591E9A824700F0BFE8 /* libsodium.la */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libsodium.la; sourceTree = "<group>"; };
|
||||
DA09795B1E9A824700F0BFE8 /* libsodium.pc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libsodium.pc; sourceTree = "<group>"; };
|
||||
DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Fabric.plist; sourceTree = "<group>"; };
|
||||
DA0CC5501EB6AE45009A8ED9 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5511EB6AE45009A8ED9 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5521EB6AE45009A8ED9 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5531EB6AE45009A8ED9 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5541EB6AE45009A8ED9 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5551EB6AE45009A8ED9 /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5561EB6AE45009A8ED9 /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5571EB6AE45009A8ED9 /* MasterPassword 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA0CC5581EB6AE45009A8ED9 /* MasterPassword 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 9.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA16B340170661DB000A0EAB /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
|
||||
DA16B343170661EE000A0EAB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPPasswordWindowController.xib; sourceTree = "<group>"; };
|
||||
@@ -854,14 +859,6 @@
|
||||
DA9261501BE1A86700369DE5 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = "<group>"; };
|
||||
DA9261531BE1A88900369DE5 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
DA9261551BE1A89600369DE5 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
DA95B51B1C477DE10067F5EF /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B51C1C477DE10067F5EF /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B51D1C477DE10067F5EF /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B51E1C477DE10067F5EF /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B51F1C477DE10067F5EF /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5201C477DE10067F5EF /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5211C477DE10067F5EF /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DA95B5221C477DE10067F5EF /* MasterPassword 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DAAA81AF195A8D1300FA30D9 /* gradient.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = gradient.png; sourceTree = "<group>"; };
|
||||
DAADCC3E19FAFFAD00987B1D /* NSNotificationCenter+PearlEasyCleanup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+PearlEasyCleanup.h"; sourceTree = "<group>"; };
|
||||
DAADCC3F19FAFFAD00987B1D /* NSPersistentStore+PearlMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPersistentStore+PearlMigration.h"; sourceTree = "<group>"; };
|
||||
@@ -916,7 +913,6 @@
|
||||
DACA24581705DF7D002C6C22 /* menu-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu-icon.png"; sourceTree = "<group>"; };
|
||||
DACA268E1705DF81002C6C22 /* SourceCodePro-ExtraLight.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-ExtraLight.otf"; sourceTree = "<group>"; };
|
||||
DACA268F1705DF81002C6C22 /* SourceCodePro-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceCodePro-Black.otf"; sourceTree = "<group>"; };
|
||||
DACA269A1705DF81002C6C22 /* Crashlytics.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Crashlytics.plist; sourceTree = "<group>"; };
|
||||
DACA29711705E1A8002C6C22 /* ciphers.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ciphers.plist; sourceTree = "<group>"; };
|
||||
DACA29721705E1A8002C6C22 /* dictionary.lst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dictionary.lst; sourceTree = "<group>"; };
|
||||
DACA29771705E2BD002C6C22 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = "<group>"; };
|
||||
@@ -995,8 +991,8 @@
|
||||
DA9261561BE1A89600369DE5 /* libz.tbd in Frameworks */,
|
||||
DA9261541BE1A88900369DE5 /* libc++.tbd in Frameworks */,
|
||||
DAADCC6A19FB00B500987B1D /* libKCOrderedAccessorFix.a in Frameworks */,
|
||||
DADD5DFA1EA173B0005E7D96 /* libsodium.a in Frameworks */,
|
||||
DA9261521BE1A86700369DE5 /* Fabric.framework in Frameworks */,
|
||||
DA09795D1E9A824700F0BFE8 /* libsodium.a in Frameworks */,
|
||||
DA9261511BE1A86700369DE5 /* SystemConfiguration.framework in Frameworks */,
|
||||
DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */,
|
||||
DAD9B5F01762CAA4001835F9 /* ServiceManagement.framework in Frameworks */,
|
||||
@@ -1132,21 +1128,17 @@
|
||||
DA0979551E9A824700F0BFE8 /* lib */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0979561E9A824700F0BFE8 /* libsodium.18.dylib */,
|
||||
DA0979571E9A824700F0BFE8 /* libsodium.a */,
|
||||
DA0979581E9A824700F0BFE8 /* libsodium.dylib */,
|
||||
DA0979591E9A824700F0BFE8 /* libsodium.la */,
|
||||
DA09795A1E9A824700F0BFE8 /* pkgconfig */,
|
||||
);
|
||||
path = lib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA09795A1E9A824700F0BFE8 /* pkgconfig */ = {
|
||||
DA0CC53C1EB57B69009A8ED9 /* Fabric */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA09795B1E9A824700F0BFE8 /* libsodium.pc */,
|
||||
DA0CC53D1EB57B69009A8ED9 /* Fabric.plist */,
|
||||
);
|
||||
path = pkgconfig;
|
||||
path = Fabric;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DA2508F819513C1400AC23F1 /* Other Frameworks */ = {
|
||||
@@ -1196,7 +1188,6 @@
|
||||
DA5BFA47147E415C00F98B1E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0974611E9961F100F0BFE8 /* libsodium.18.dylib */,
|
||||
DA09745D1E99586600F0BFE8 /* libxml2.tbd */,
|
||||
DA6701B716406A4100B61001 /* Accounts.framework */,
|
||||
DA16B340170661DB000A0EAB /* Carbon.framework */,
|
||||
@@ -1230,7 +1221,7 @@
|
||||
children = (
|
||||
DA5E5CB21724A667003798D8 /* Mac */,
|
||||
DA771FE51E6E15A1004D7EDE /* MasterPassword-Prefix.pch */,
|
||||
DA95B51A1C477DE10067F5EF /* MasterPassword.xcdatamodeld */,
|
||||
DA0CC54F1EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld */,
|
||||
DA5E5C971724A667003798D8 /* MPAlgorithm.h */,
|
||||
DA5E5C981724A667003798D8 /* MPAlgorithm.m */,
|
||||
DA5E5C991724A667003798D8 /* MPAlgorithmV0.h */,
|
||||
@@ -1790,8 +1781,8 @@
|
||||
DACA23B41705DF7D002C6C22 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DA0CC53C1EB57B69009A8ED9 /* Fabric */,
|
||||
DA09745F1E995EB500F0BFE8 /* mpw_tests.xml */,
|
||||
DACA26991705DF81002C6C22 /* Crashlytics */,
|
||||
DACA29701705E1A8002C6C22 /* Data */,
|
||||
DACA23B51705DF7D002C6C22 /* Media */,
|
||||
);
|
||||
@@ -1874,14 +1865,6 @@
|
||||
path = Fonts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DACA26991705DF81002C6C22 /* Crashlytics */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DACA269A1705DF81002C6C22 /* Crashlytics.plist */,
|
||||
);
|
||||
path = Crashlytics;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DACA29701705E1A8002C6C22 /* Data */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2104,7 +2087,7 @@
|
||||
DA5BFA42147E415C00F98B1E /* Resources */,
|
||||
DAD9B5EE1762CA3A001835F9 /* Copy LoginHelper */,
|
||||
DA6556E314D55F3000841C99 /* Run Script: GIT version -> Info.plist */,
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */,
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -2313,13 +2296,13 @@
|
||||
DACA27331705DF81002C6C22 /* avatar-12@2x.png in Resources */,
|
||||
DACA27341705DF81002C6C22 /* avatar-2@2x.png in Resources */,
|
||||
DA60717D195D040500CA98B5 /* icon_gear@2x.png in Resources */,
|
||||
DA0CC53E1EB57B69009A8ED9 /* Fabric.plist in Resources */,
|
||||
DACA27351705DF81002C6C22 /* avatar-11.png in Resources */,
|
||||
DACA27361705DF81002C6C22 /* avatar-0@2x.png in Resources */,
|
||||
DACA27371705DF81002C6C22 /* avatar-10@2x.png in Resources */,
|
||||
DACA27381705DF81002C6C22 /* menu-icon.png in Resources */,
|
||||
DACA29671705DF81002C6C22 /* SourceCodePro-ExtraLight.otf in Resources */,
|
||||
DACA29681705DF81002C6C22 /* SourceCodePro-Black.otf in Resources */,
|
||||
DACA296F1705DF81002C6C22 /* Crashlytics.plist in Resources */,
|
||||
DACA29731705E1A8002C6C22 /* ciphers.plist in Resources */,
|
||||
DACA29741705E1A8002C6C22 /* dictionary.lst in Resources */,
|
||||
DA5E5D081724A667003798D8 /* MasterPassword.entitlements in Resources */,
|
||||
@@ -2345,7 +2328,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = "/bin/sh -e";
|
||||
shellScript = "cd External/libsodium\n[[ -d libsodium-osx ]] && exit\n\n# Xcode misinterpretes autogen.sh's stderr output as errors so we try to silence it.\n[[ -e configure ]] || { err=$(./autogen.sh 2>&1 >&3); } 3>&1 || { x=$?; echo >&2 \"$err\"; exit $x; }\n./dist-build/osx.sh";
|
||||
shellScript = "cd External/libsodium\nif ! [[ -d libsodium-osx ]]; then\n # Xcode misinterpretes autogen.sh's stderr output as errors so we try to silence it.\n [[ -e configure ]] || { err=$(./autogen.sh 2>&1 >&3); } 3>&1 || { x=$?; echo >&2 \"$err\"; exit $x; }\n./dist-build/osx.sh\nfi\n\n# Xcode incorrectly links the dynamic libraries if they're present instead of linking the .a.\nrm -f libsodium-osx/lib/*.dylib";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DA4EF9CB19FD4B600032ECB5 /* Run Script: genassets */ = {
|
||||
@@ -2377,19 +2360,19 @@
|
||||
shellPath = "/bin/sh -e";
|
||||
shellScript = "exec Scripts/updatePlist";
|
||||
};
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Crashlytics */ = {
|
||||
DAD3125D155288AA00A3F9ED /* Run Script: Fabric */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script: Crashlytics";
|
||||
name = "Run Script: Fabric";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = "/bin/sh -e";
|
||||
shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Crashlytics/Crashlytics.plist)\n[[ $apiKey ]] && External/Mac/Fabric.framework/run \"$apiKey\" 410fb41450e3a2e50fa8357682d812ecd3e1846f2141a99bdb9d3a6a981ad69c";
|
||||
shellScript = "[[ $DEPLOYMENT_LOCATION != YES ]] && exit\n\napiKey=$(/usr/libexec/PlistBuddy -c \"Print :'API Key'\" Resources/Fabric/Fabric.plist)\n[[ $apiKey ]] && External/Mac/Fabric.framework/run \"$apiKey\" 410fb41450e3a2e50fa8357682d812ecd3e1846f2141a99bdb9d3a6a981ad69c";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
@@ -2426,8 +2409,8 @@
|
||||
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
|
||||
DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */,
|
||||
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */,
|
||||
DA95B5231C477DE10067F5EF /* MasterPassword.xcdatamodeld in Sources */,
|
||||
DA6774291A4746AF004F356A /* mpw-algorithm.c in Sources */,
|
||||
DA0CC5591EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld in Sources */,
|
||||
93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */,
|
||||
DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */,
|
||||
DA4DAE951A7D8117003E5423 /* MPTypes.m in Sources */,
|
||||
@@ -2586,7 +2569,6 @@
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"ADHOC=1",
|
||||
"$(inherited)",
|
||||
"NDEBUG=1",
|
||||
"NS_BLOCK_ASSERTIONS=1",
|
||||
@@ -2856,7 +2838,6 @@
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"ADHOC=1",
|
||||
"$(inherited)",
|
||||
"NDEBUG=1",
|
||||
"NS_BLOCK_ASSERTIONS=1",
|
||||
@@ -3149,19 +3130,20 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
DA95B51A1C477DE10067F5EF /* MasterPassword.xcdatamodeld */ = {
|
||||
DA0CC54F1EB6AE45009A8ED9 /* MasterPassword.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DA95B51B1C477DE10067F5EF /* MasterPassword 1.xcdatamodel */,
|
||||
DA95B51C1C477DE10067F5EF /* MasterPassword 2.xcdatamodel */,
|
||||
DA95B51D1C477DE10067F5EF /* MasterPassword 3.xcdatamodel */,
|
||||
DA95B51E1C477DE10067F5EF /* MasterPassword 4.xcdatamodel */,
|
||||
DA95B51F1C477DE10067F5EF /* MasterPassword 5.xcdatamodel */,
|
||||
DA95B5201C477DE10067F5EF /* MasterPassword 6.xcdatamodel */,
|
||||
DA95B5211C477DE10067F5EF /* MasterPassword 7.xcdatamodel */,
|
||||
DA95B5221C477DE10067F5EF /* MasterPassword 8.xcdatamodel */,
|
||||
DA0CC5501EB6AE45009A8ED9 /* MasterPassword 1.xcdatamodel */,
|
||||
DA0CC5511EB6AE45009A8ED9 /* MasterPassword 2.xcdatamodel */,
|
||||
DA0CC5521EB6AE45009A8ED9 /* MasterPassword 3.xcdatamodel */,
|
||||
DA0CC5531EB6AE45009A8ED9 /* MasterPassword 4.xcdatamodel */,
|
||||
DA0CC5541EB6AE45009A8ED9 /* MasterPassword 5.xcdatamodel */,
|
||||
DA0CC5551EB6AE45009A8ED9 /* MasterPassword 6.xcdatamodel */,
|
||||
DA0CC5561EB6AE45009A8ED9 /* MasterPassword 7.xcdatamodel */,
|
||||
DA0CC5571EB6AE45009A8ED9 /* MasterPassword 8.xcdatamodel */,
|
||||
DA0CC5581EB6AE45009A8ED9 /* MasterPassword 9.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DA95B5221C477DE10067F5EF /* MasterPassword 8.xcdatamodel */;
|
||||
currentVersion = DA0CC5581EB6AE45009A8ED9 /* MasterPassword 9.xcdatamodel */;
|
||||
path = MasterPassword.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
||||
@@ -37,13 +37,13 @@ setSettingWithTitle() {
|
||||
|
||||
case $PLATFORM_NAME in
|
||||
macosx) platform=mac ;;
|
||||
ios) platform=ios ;;
|
||||
iphone*) platform=ios ;;
|
||||
*) ftl 'ERROR: Unknown platform: %s.' "$PLATFORM_NAME"; exit 1 ;;
|
||||
esac
|
||||
|
||||
description=$(git describe --always --dirty --long --match "*-$platform-*")
|
||||
description=$(git describe --always --dirty --long --match "*-$platform*")
|
||||
version=${description%-g*} build=${version##*-} version=${version%-$build}
|
||||
version=${version//-*-/.} release=${version%%-*} commit=${description##*-g}
|
||||
version=${version//-$platform/} version=${version//-/.} commit=${description##*-g}
|
||||
|
||||
addPlistWithKey GITDescription string "$description"
|
||||
setPlistWithKey CFBundleVersion "$(hr "${version%%.*}" 14).${version#*.}"
|
||||
@@ -55,14 +55,14 @@ setSettingWithTitle "Copyright" "$(getPlistWithKey NSHumanReadableCopyright)"
|
||||
|
||||
if [[ $DEPLOYMENT_LOCATION = YES ]]; then
|
||||
# This build is a release. Do some release checks.
|
||||
crashlyticsPlist="$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/Crashlytics.plist"
|
||||
fabricPlist="$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/Fabric.plist"
|
||||
passed=1
|
||||
[[ $description != *-dirty ]] || \
|
||||
{ passed=0; err 'ERROR: Cannot release a dirty version, first commit any changes.'; }
|
||||
[[ $build == 0 ]] || \
|
||||
{ passed=0; err 'ERROR: Commit is not tagged for release, first tag accordingly.'; }
|
||||
[[ -r "$crashlyticsPlist" && $(PlistBuddy -c "Print :'API Key'" "$crashlyticsPlist" 2>/dev/null) ]] || \
|
||||
{ passed=0; err 'ERROR: Cannot release: Crashlytics API key is missing.'; }
|
||||
[[ -r "$fabricPlist" && $(PlistBuddy -c "Print :'API Key'" "$fabricPlist" 2>/dev/null) ]] || \
|
||||
{ passed=0; err 'ERROR: Cannot release: Fabric API key is missing.'; }
|
||||
(( passed )) || \
|
||||
{ ftl "Failed to pass release checks. Fix the above errors and re-try. Aborting."; exit 1; }
|
||||
fi
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
//==============================================================================
|
||||
|
||||
#import "MPKey.h"
|
||||
#import "MPStoredSiteEntity.h"
|
||||
#import "MPGeneratedSiteEntity.h"
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPStoredSiteEntity+CoreDataClass.h"
|
||||
#import "MPGeneratedSiteEntity+CoreDataClass.h"
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
#import "mpw-algorithm.h"
|
||||
|
||||
#define MPAlgorithmDefaultVersion MPAlgorithmVersionCurrent
|
||||
@@ -58,6 +58,7 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
|
||||
- (Class)classOfType:(MPSiteType)type;
|
||||
- (NSArray *)allTypes;
|
||||
- (NSArray *)allTypesStartingWith:(MPSiteType)startingType;
|
||||
- (MPSiteType)defaultType;
|
||||
- (MPSiteType)nextType:(MPSiteType)type;
|
||||
- (MPSiteType)previousType:(MPSiteType)type;
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
migrationRequest.predicate = [NSPredicate predicateWithFormat:@"version_ < %d AND user == %@", self.version, user];
|
||||
NSArray *migrationSites = [moc executeFetchRequest:migrationRequest error:&error];
|
||||
if (!migrationSites) {
|
||||
err( @"While looking for sites to migrate: %@", [error fullDescription] );
|
||||
MPError( error, @"While looking for sites to migrate." );
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -286,6 +286,11 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
return allTypes;
|
||||
}
|
||||
|
||||
- (MPSiteType)defaultType {
|
||||
|
||||
return MPSiteTypeGeneratedLong;
|
||||
}
|
||||
|
||||
- (MPSiteType)nextType:(MPSiteType)type {
|
||||
|
||||
switch (type) {
|
||||
@@ -309,9 +314,9 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
return MPSiteTypeStoredDevicePrivate;
|
||||
case MPSiteTypeStoredDevicePrivate:
|
||||
return MPSiteTypeGeneratedPhrase;
|
||||
default:
|
||||
return MPSiteTypeGeneratedLong;
|
||||
}
|
||||
|
||||
return [self defaultType];
|
||||
}
|
||||
|
||||
- (MPSiteType)previousType:(MPSiteType)type {
|
||||
@@ -430,58 +435,38 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
- (NSString *)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolveLoginForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSString *)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolvePasswordForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSString *)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolveAnswerForSite:site usingKey:siteKey result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSString *)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey {
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter( group );
|
||||
__block NSString *result = nil;
|
||||
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
|
||||
result = result_;
|
||||
dispatch_group_leave( group );
|
||||
}];
|
||||
dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return result;
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[self resolveAnswerForQuestion:question usingKey:siteKey result:^(NSString *result_) {
|
||||
setResult( result_ );
|
||||
}];
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||
@@ -498,10 +483,12 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
resultBlock( loginName || !loginGenerated? loginName:
|
||||
[algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
|
||||
} );
|
||||
if (!loginGenerated || [loginName length])
|
||||
resultBlock( loginName );
|
||||
else
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generateLoginForSiteNamed:name usingKey:siteKey] );
|
||||
} );
|
||||
}
|
||||
|
||||
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
|
||||
@@ -533,9 +520,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generatePasswordForSiteNamed:name ofType:type withCounter:counter usingKey:siteKey] );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
@@ -549,9 +535,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
|
||||
NSData *encryptedContent = ((MPStoredSiteEntity *)site).contentObject;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
@@ -563,9 +548,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
|
||||
NSData *encryptedContent = [PearlKeyChain dataOfItemForQuery:siteQuery];
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [self decryptContent:encryptedContent usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [self decryptContent:encryptedContent usingKey:siteKey] );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
@@ -584,9 +568,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
else
|
||||
algorithm = site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:nil usingKey:siteKey] );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -605,9 +588,8 @@ NSOperationQueue *_mpwQueue = nil;
|
||||
else if ([[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateAnswers])
|
||||
algorithm = question.site.algorithm;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSString *result = [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey];
|
||||
resultBlock( result );
|
||||
PearlNotMainQueue( ^{
|
||||
resultBlock( [algorithm generateAnswerForSiteNamed:name onQuestion:keyword usingKey:siteKey] );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
@@ -25,17 +25,20 @@
|
||||
#define MPProductTouchID @"com.lyndir.masterpassword.products.touchid"
|
||||
#define MPProductFuel @"com.lyndir.masterpassword.products.fuel"
|
||||
|
||||
#define MP_FUEL_HOURLY_RATE 30.f /* Tier 1 purchases/h ~> USD/h */
|
||||
#define MP_FUEL_HOURLY_RATE 40.f /* payment in tier 1 purchases / h (≅ USD / h) */
|
||||
|
||||
@protocol MPInAppDelegate
|
||||
|
||||
- (void)updateWithProducts:(NSArray /* SKProduct */ *)products;
|
||||
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction;
|
||||
- (void)updateWithProducts:(NSDictionary<NSString *, SKProduct *> *)products
|
||||
transactions:(NSDictionary<NSString *, SKPaymentTransaction *> *)transactions;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPAppDelegate_Shared(InApp)
|
||||
|
||||
- (NSDictionary<NSString *, SKProduct *> *)products;
|
||||
- (NSDictionary<NSString *, SKPaymentTransaction *> *)transactions;
|
||||
|
||||
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate;
|
||||
- (void)removeProductsObserver:(id<MPInAppDelegate>)delegate;
|
||||
|
||||
|
||||
@@ -23,9 +23,20 @@
|
||||
|
||||
@implementation MPAppDelegate_Shared(InApp)
|
||||
|
||||
PearlAssociatedObjectProperty( NSArray*, Products, products );
|
||||
PearlAssociatedObjectProperty( NSDictionary*, Products, products );
|
||||
|
||||
PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObservers );
|
||||
|
||||
- (NSDictionary<NSString *, SKPaymentTransaction *> *)transactions {
|
||||
|
||||
NSMutableDictionary<NSString *, SKPaymentTransaction *> *transactions =
|
||||
[NSMutableDictionary dictionaryWithCapacity:self.paymentQueue.transactions.count];
|
||||
for (SKPaymentTransaction *transaction in self.paymentQueue.transactions)
|
||||
transactions[transaction.payment.productIdentifier] = transaction;
|
||||
|
||||
return transactions;
|
||||
}
|
||||
|
||||
- (void)registerProductsObserver:(id<MPInAppDelegate>)delegate {
|
||||
|
||||
if (!self.productObservers)
|
||||
@@ -33,7 +44,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
[self.productObservers addObject:delegate];
|
||||
|
||||
if (self.products)
|
||||
[delegate updateWithProducts:self.products];
|
||||
[delegate updateWithProducts:self.products transactions:[self transactions]];
|
||||
else
|
||||
[self reloadProducts];
|
||||
}
|
||||
@@ -75,7 +86,7 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
// Consumable product.
|
||||
return NO;
|
||||
|
||||
#if ADHOC || DEBUG
|
||||
#if DEBUG
|
||||
// All features are unlocked for beta / debug / mac versions.
|
||||
return YES;
|
||||
#else
|
||||
@@ -101,11 +112,13 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
}
|
||||
#endif
|
||||
|
||||
for (SKProduct *product in self.products)
|
||||
for (SKProduct *product in [self.products allValues])
|
||||
if ([product.productIdentifier isEqualToString:productIdentifier]) {
|
||||
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
|
||||
payment.quantity = quantity;
|
||||
[[self paymentQueue] addPayment:payment];
|
||||
if (payment) {
|
||||
payment.quantity = quantity;
|
||||
[[self paymentQueue] addPayment:payment];
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -114,15 +127,22 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
|
||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
|
||||
|
||||
inf( @"products: %@, invalid: %@", response.products, response.invalidProductIdentifiers );
|
||||
self.products = response.products;
|
||||
if ([response.invalidProductIdentifiers count])
|
||||
inf( @"Invalid products: %@", response.invalidProductIdentifiers );
|
||||
|
||||
NSMutableDictionary *products = [NSMutableDictionary dictionaryWithCapacity:[response.products count]];
|
||||
for (SKProduct *product in response.products)
|
||||
products[product.productIdentifier] = product;
|
||||
self.products = products;
|
||||
|
||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
||||
[productObserver updateWithProducts:self.products];
|
||||
[productObserver updateWithProducts:self.products transactions:[self transactions]];
|
||||
}
|
||||
|
||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
|
||||
|
||||
MPError( error, @"StoreKit request (%@) failed.", request );
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
[PearlAlert showAlertWithTitle:@"Purchase Failed" message:
|
||||
strf( @"%@\n\n%@", error.localizedDescription,
|
||||
@@ -131,7 +151,6 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
cancelTitle:@"OK" otherTitles:nil];
|
||||
#else
|
||||
#endif
|
||||
err( @"StoreKit request (%@) failed: %@", request, [error fullDescription] );
|
||||
}
|
||||
|
||||
- (void)requestDidFinish:(SKRequest *)request {
|
||||
@@ -145,23 +164,41 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
|
||||
for (SKPaymentTransaction *transaction in transactions) {
|
||||
dbg( @"transaction updated: %@ -> %d", transaction.payment.productIdentifier, (int)(transaction.transactionState) );
|
||||
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStatePurchased: {
|
||||
inf( @"purchased: %@", transaction.payment.productIdentifier );
|
||||
inf( @"Purchased: %@", transaction.payment.productIdentifier );
|
||||
NSMutableDictionary *attributes = [NSMutableDictionary new];
|
||||
|
||||
if ([transaction.payment.productIdentifier isEqualToString:MPProductFuel]) {
|
||||
float currentFuel = [[MPiOSConfig get].developmentFuelRemaining floatValue];
|
||||
float purchasedFuel = transaction.payment.quantity / MP_FUEL_HOURLY_RATE;
|
||||
[MPiOSConfig get].developmentFuelRemaining = @(currentFuel + purchasedFuel);
|
||||
if (![MPiOSConfig get].developmentFuelChecked || currentFuel < DBL_EPSILON)
|
||||
[MPiOSConfig get].developmentFuelChecked = [NSDate date];
|
||||
[attributes addEntriesFromDictionary:@{
|
||||
@"currentFuel" : @(currentFuel),
|
||||
@"purchasedFuel": @(purchasedFuel),
|
||||
}];
|
||||
}
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
|
||||
forKey:transaction.payment.productIdentifier];
|
||||
[queue finishTransaction:transaction];
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
for (int q = 0; q < transaction.payment.quantity; ++q)
|
||||
[Answers logPurchaseWithPrice:product.price currency:[product.priceLocale objectForKey:NSLocaleCurrencyCode]
|
||||
success:@YES itemName:product.localizedTitle itemType:@"InApp"
|
||||
itemId:product.productIdentifier customAttributes:attributes];
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SKPaymentTransactionStateRestored: {
|
||||
inf( @"restored: %@", transaction.payment.productIdentifier );
|
||||
inf( @"Restored: %@", transaction.payment.productIdentifier );
|
||||
[[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier
|
||||
forKey:transaction.payment.productIdentifier];
|
||||
[queue finishTransaction:transaction];
|
||||
@@ -171,21 +208,38 @@ PearlAssociatedObjectProperty( NSMutableArray*, ProductObservers, productObserve
|
||||
case SKPaymentTransactionStateDeferred:
|
||||
break;
|
||||
case SKPaymentTransactionStateFailed:
|
||||
err( @"Transaction failed: %@, reason: %@", transaction.payment.productIdentifier, [transaction.error fullDescription] );
|
||||
MPError( transaction.error, @"Transaction failed: %@.", transaction.payment.productIdentifier );
|
||||
[queue finishTransaction:transaction];
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
SKProduct *product = self.products[transaction.payment.productIdentifier];
|
||||
[Answers logPurchaseWithPrice:product.price currency:[product.priceLocale objectForKey:NSLocaleCurrencyCode]
|
||||
success:@NO itemName:product.localizedTitle itemType:@"InApp" itemId:product.productIdentifier
|
||||
customAttributes:@{
|
||||
@"state" : @"Failed",
|
||||
@"quantity": @(transaction.payment.quantity),
|
||||
@"reason" : [transaction.error localizedFailureReason]?: [transaction.error localizedDescription],
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
||||
[productObserver updateWithTransaction:transaction];
|
||||
}
|
||||
|
||||
if (![[NSUserDefaults standardUserDefaults] synchronize])
|
||||
wrn( @"Couldn't synchronize after transaction updates." );
|
||||
|
||||
NSMutableDictionary<NSString *, SKPaymentTransaction *> *allTransactions = [[self transactions] mutableCopy];
|
||||
for (SKPaymentTransaction *transaction in transactions)
|
||||
allTransactions[transaction.payment.productIdentifier] = transaction;
|
||||
for (id<MPInAppDelegate> productObserver in self.productObservers)
|
||||
[productObserver updateWithProducts:self.products transactions:allTransactions];
|
||||
}
|
||||
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
|
||||
|
||||
err( @"StoreKit restore failed: %@", [error fullDescription] );
|
||||
MPError( error, @"StoreKit restore failed." );
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,18 +16,19 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Crashlytics/Answers.h>
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
|
||||
@interface MPAppDelegate_Shared()
|
||||
|
||||
@property(strong, nonatomic) MPKey *key;
|
||||
@property(strong, atomic) MPKey *key;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPAppDelegate_Shared(Key)
|
||||
|
||||
static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigin *keyOrigin) {
|
||||
- (NSDictionary *)createKeyQueryforUser:(MPUserEntity *)user origin:(out MPKeyOrigin *)keyOrigin {
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
if (user.touchID && kSecUseAuthenticationUI) {
|
||||
@@ -37,17 +38,17 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
CFErrorRef acError = NULL;
|
||||
id accessControl = (__bridge_transfer id)SecAccessControlCreateWithFlags( kCFAllocatorDefault,
|
||||
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlTouchIDCurrentSet, &acError );
|
||||
if (!accessControl || acError)
|
||||
err( @"Could not use TouchID on this device: %@", acError );
|
||||
if (!accessControl)
|
||||
MPError( (__bridge_transfer NSError *)acError, @"Could not use TouchID on this device." );
|
||||
|
||||
else
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:@{
|
||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||
(__bridge id)kSecAttrAccessControl : accessControl,
|
||||
(__bridge id)kSecUseAuthenticationUI : (__bridge id)kSecUseAuthenticationUIAllow,
|
||||
(__bridge id)kSecUseOperationPrompt :
|
||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||
(__bridge id)kSecAttrAccessControl : accessControl,
|
||||
(__bridge id)kSecUseAuthenticationUI: (__bridge id)kSecUseAuthenticationUIAllow,
|
||||
(__bridge id)kSecUseOperationPrompt :
|
||||
strf( @"Access %@'s master password.", user.name ),
|
||||
}
|
||||
matches:nil];
|
||||
@@ -59,10 +60,10 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
|
||||
return [PearlKeyChain createQueryForClass:kSecClassGenericPassword
|
||||
attributes:@{
|
||||
(__bridge id)kSecAttrService: @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount: user.name?: @"",
|
||||
(__bridge id)kSecAttrService : @"Saved Master Password",
|
||||
(__bridge id)kSecAttrAccount : user.name?: @"",
|
||||
#if TARGET_OS_IPHONE
|
||||
(__bridge id)kSecAttrAccessible : (__bridge id)(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly?: kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
|
||||
(__bridge id)kSecAttrAccessible: (__bridge id)(kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly?: kSecAttrAccessibleWhenUnlockedThisDeviceOnly),
|
||||
#endif
|
||||
}
|
||||
matches:nil];
|
||||
@@ -71,15 +72,24 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
- (MPKey *)loadSavedKeyFor:(MPUserEntity *)user {
|
||||
|
||||
MPKeyOrigin keyOrigin;
|
||||
NSDictionary *keyQuery = createKeyQuery( user, NO, &keyOrigin );
|
||||
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery];
|
||||
if (!keyData) {
|
||||
NSDictionary *keyQuery = [self createKeyQueryforUser:user origin:&keyOrigin];
|
||||
id<MPAlgorithm> keyAlgorithm = user.algorithm;
|
||||
MPKey *key = [[MPKey alloc] initForFullName:user.name withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) {
|
||||
return ![algorithm isEqual:keyAlgorithm]? nil:
|
||||
PearlMainQueueAwait( (id)^{
|
||||
return [PearlKeyChain dataOfItemForQuery:keyQuery];
|
||||
} );
|
||||
} keyOrigin:keyOrigin];
|
||||
|
||||
if ([key keyIDForAlgorithm:user.algorithm])
|
||||
inf( @"Found key in keychain for user: %@", user.userID );
|
||||
|
||||
else {
|
||||
inf( @"No key found in keychain for user: %@", user.userID );
|
||||
return nil;
|
||||
key = nil;
|
||||
}
|
||||
|
||||
inf( @"Found key in keychain for user: %@", user.userID );
|
||||
return [[MPKey alloc] initForFullName:user.name withKeyData:keyData forAlgorithm:user.algorithm keyOrigin:keyOrigin];
|
||||
return key;
|
||||
}
|
||||
|
||||
- (void)storeSavedKeyFor:(MPUserEntity *)user {
|
||||
@@ -90,7 +100,7 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
[self forgetSavedKeyFor:user];
|
||||
|
||||
inf( @"Saving key in keychain for user: %@", user.userID );
|
||||
[PearlKeyChain addOrUpdateItemForQuery:createKeyQuery( user, YES, nil )
|
||||
[PearlKeyChain addOrUpdateItemForQuery:[self createKeyQueryforUser:user origin:nil]
|
||||
withAttributes:@{ (__bridge id)kSecValueData: keyData }];
|
||||
}
|
||||
}
|
||||
@@ -98,7 +108,7 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
|
||||
- (void)forgetSavedKeyFor:(MPUserEntity *)user {
|
||||
|
||||
OSStatus result = [PearlKeyChain deleteItemForQuery:createKeyQuery( user, NO, nil )];
|
||||
OSStatus result = [PearlKeyChain deleteItemForQuery:[self createKeyQueryforUser:user origin:nil]];
|
||||
if (result == noErr) {
|
||||
inf( @"Removed key from keychain for user: %@", user.userID );
|
||||
|
||||
@@ -168,6 +178,14 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
else
|
||||
dbg( @"Automatic login failed for user: %@", user.userID );
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
[Answers logLoginWithMethod:password? @"Password": @"Automatic" success:@NO customAttributes:@{
|
||||
@"algorithm": @(user.algorithm.version),
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
inf( @"Logged in user: %@", user.userID );
|
||||
@@ -189,8 +207,11 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
@try {
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
[[Crashlytics sharedInstance] setObjectValue:user.userID forKey:@"username"];
|
||||
[[Crashlytics sharedInstance] setUserName:user.userID];
|
||||
|
||||
[Answers logLoginWithMethod:password? @"Password": @"Automatic" success:@YES customAttributes:@{
|
||||
@"algorithm": @(user.algorithm.version),
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -230,28 +251,22 @@ static NSDictionary *createKeyQuery(MPUserEntity *user, BOOL newItem, MPKeyOrigi
|
||||
NSString *content;
|
||||
while (!(content = [site.algorithm storedPasswordForSite:(MPStoredSiteEntity *)site usingKey:recoverKey])) {
|
||||
// Failed to decrypt site with the current recoveryKey. Ask user for a new one to use.
|
||||
__block NSString *masterPassword = nil;
|
||||
NSString *masterPassword = nil;
|
||||
|
||||
#ifdef PEARL_UIKIT
|
||||
dispatch_group_t recoverPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter( recoverPasswordGroup );
|
||||
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
|
||||
message:PearlString( @"Your old master password is required to migrate the stored password for %@",
|
||||
site.name )
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
masterPassword = PearlAwait( ^(void (^setResult)(id)) {
|
||||
[PearlAlert showAlertWithTitle:@"Enter Old Master Password"
|
||||
message:PearlString(
|
||||
@"Your old master password is required to migrate the stored password for %@",
|
||||
site.name )
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
// Don't Migrate
|
||||
return;
|
||||
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
@finally {
|
||||
dispatch_group_leave( recoverPasswordGroup );
|
||||
}
|
||||
} cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
|
||||
dispatch_group_wait( recoverPasswordGroup, DISPATCH_TIME_FOREVER );
|
||||
setResult( nil );
|
||||
else
|
||||
setResult( [alert_ textFieldAtIndex:0].text );
|
||||
} cancelTitle:@"Don't Migrate" otherTitles:@"Migrate", nil];
|
||||
} );
|
||||
#endif
|
||||
if (!masterPassword)
|
||||
// Don't Migrate
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
#endif
|
||||
|
||||
@property(strong, nonatomic, readonly) MPKey *key;
|
||||
@property(strong, nonatomic, readonly) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, nonatomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
@property(strong, atomic, readonly) MPKey *key;
|
||||
@property(strong, atomic, readonly) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, atomic, readonly) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
|
||||
+ (instancetype)get;
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
@interface MPAppDelegate_Shared()
|
||||
|
||||
@property(strong, nonatomic) MPKey *key;
|
||||
@property(strong, nonatomic) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, nonatomic) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
@property(strong, atomic) MPKey *key;
|
||||
@property(strong, atomic) NSManagedObjectID *activeUserOID;
|
||||
@property(strong, atomic) NSPersistentStoreCoordinator *storeCoordinator;
|
||||
|
||||
@end
|
||||
|
||||
@@ -74,14 +74,11 @@
|
||||
|
||||
- (void)setActiveUser:(MPUserEntity *)activeUser {
|
||||
|
||||
NSError *error;
|
||||
if (activeUser.objectID.isTemporaryID && ![activeUser.managedObjectContext obtainPermanentIDsForObjects:@[ activeUser ] error:&error])
|
||||
err( @"Failed to obtain a permanent object ID after setting active user: %@", [error fullDescription] );
|
||||
|
||||
self.activeUserOID = activeUser.objectID;
|
||||
self.activeUserOID = activeUser.permanentObjectID;
|
||||
}
|
||||
|
||||
- (void)handleCoordinatorError:(NSError *)error {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -35,6 +35,7 @@ typedef NS_ENUM( NSUInteger, MPImportResult ) {
|
||||
+ (BOOL)managedObjectContextForMainThreadPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *mainContext))mocBlock;
|
||||
+ (BOOL)managedObjectContextPerformBlock:(void ( ^ )(NSManagedObjectContext *context))mocBlock;
|
||||
+ (BOOL)managedObjectContextPerformBlockAndWait:(void ( ^ )(NSManagedObjectContext *context))mocBlock;
|
||||
+ (id)managedObjectContextChanged:(void ( ^ )(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects))changedBlock;
|
||||
|
||||
- (MPFixableResult)findAndFixInconsistenciesSaveInContext:(NSManagedObjectContext *)context;
|
||||
- (void)deleteAndResetStore;
|
||||
|
||||
@@ -131,6 +131,25 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (id)managedObjectContextChanged:(void ( ^ )(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects))changedBlock {
|
||||
|
||||
NSManagedObjectContext *privateManagedObjectContextIfReady = [[self get] privateManagedObjectContextIfReady];
|
||||
if (!privateManagedObjectContextIfReady)
|
||||
return nil;
|
||||
|
||||
return PearlAddNotificationObserver( NSManagedObjectContextObjectsDidChangeNotification, privateManagedObjectContextIfReady, nil,
|
||||
^(id host, NSNotification *note) {
|
||||
NSMutableDictionary *affectedObjects = [NSMutableDictionary new];
|
||||
for (NSManagedObject *object in note.userInfo[NSInsertedObjectsKey])
|
||||
affectedObjects[object.objectID] = NSInsertedObjectsKey;
|
||||
for (NSManagedObject *object in note.userInfo[NSUpdatedObjectsKey])
|
||||
affectedObjects[object.objectID] = NSUpdatedObjectsKey;
|
||||
for (NSManagedObject *object in note.userInfo[NSDeletedObjectsKey])
|
||||
affectedObjects[object.objectID] = NSDeletedObjectsKey;
|
||||
changedBlock( affectedObjects );
|
||||
} );
|
||||
}
|
||||
|
||||
- (NSManagedObjectContext *)mainManagedObjectContextIfReady {
|
||||
|
||||
[self loadStore];
|
||||
@@ -196,19 +215,21 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
self.mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
|
||||
self.mainManagedObjectContext.parentContext = self.privateManagedObjectContext;
|
||||
|
||||
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
||||
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
||||
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainManagedObjectContext, NSNotification *note) {
|
||||
[mainManagedObjectContext performBlock:^{
|
||||
@try {
|
||||
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:note];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
err( @"While merging changes:\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
} );
|
||||
if ([self.mainManagedObjectContext respondsToSelector:@selector( automaticallyMergesChangesFromParent )]) // iOS 10+
|
||||
self.mainManagedObjectContext.automaticallyMergesChangesFromParent = YES;
|
||||
else
|
||||
// When privateManagedObjectContext is saved, import the changes into mainManagedObjectContext.
|
||||
PearlAddNotificationObserverTo( self.mainManagedObjectContext, NSManagedObjectContextDidSaveNotification,
|
||||
self.privateManagedObjectContext, nil, ^(NSManagedObjectContext *mainContext, NSNotification *note) {
|
||||
[mainContext performBlock:^{
|
||||
@try {
|
||||
[mainContext mergeChangesFromContextDidSaveNotification:note];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
err( @"While merging changes:\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
} );
|
||||
|
||||
|
||||
// Create a new store coordinator.
|
||||
@@ -216,7 +237,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSURL *localStoreURL = [self localStoreURL];
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtURL:[localStoreURL URLByDeletingLastPathComponent]
|
||||
withIntermediateDirectories:YES attributes:nil error:&error]) {
|
||||
err( @"Couldn't create our application support directory: %@", [error fullDescription] );
|
||||
MPError( error, @"Couldn't create our application support directory." );
|
||||
return;
|
||||
}
|
||||
if (![self.storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self localStoreURL]
|
||||
@@ -225,7 +246,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSInferMappingModelAutomaticallyOption : @YES,
|
||||
STORE_OPTIONS
|
||||
} error:&error]) {
|
||||
err( @"Failed to open store: %@", [error fullDescription] );
|
||||
MPError( error, @"Failed to open store." );
|
||||
self.storeCorrupted = @YES;
|
||||
[self handleCoordinatorError:error];
|
||||
return;
|
||||
@@ -234,12 +255,15 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, UIApp, [NSOperationQueue mainQueue],
|
||||
^(MPAppDelegate_Shared *self, NSNotification *note) {
|
||||
[self.mainManagedObjectContext saveToStore];
|
||||
} );
|
||||
#else
|
||||
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, NSApp, [NSOperationQueue mainQueue],
|
||||
#endif
|
||||
^(MPAppDelegate_Shared *self, NSNotification *note) {
|
||||
[self.mainManagedObjectContext saveToStore];
|
||||
} );
|
||||
[self.mainManagedObjectContext saveToStore];
|
||||
} );
|
||||
#endif
|
||||
|
||||
// Perform a data sanity check on the newly loaded store to find and fix any issues.
|
||||
if ([[MPConfig get].checkInconsistency boolValue])
|
||||
@@ -265,10 +289,10 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSError *error = nil;
|
||||
for (NSPersistentStore *store in self.storeCoordinator.persistentStores) {
|
||||
if (![self.storeCoordinator removePersistentStore:store error:&error])
|
||||
err( @"Couldn't remove persistence store from coordinator: %@", [error fullDescription] );
|
||||
MPError( error, @"Couldn't remove persistence store from coordinator." );
|
||||
}
|
||||
if (![[NSFileManager defaultManager] removeItemAtURL:self.localStoreURL error:&error])
|
||||
err( @"Couldn't remove persistence store at URL %@: %@", self.localStoreURL, [error fullDescription] );
|
||||
MPError( error, @"Couldn't remove persistence store at URL %@.", self.localStoreURL );
|
||||
|
||||
[self loadStore];
|
||||
}
|
||||
@@ -286,7 +310,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
fetchRequest.entity = entity;
|
||||
NSArray *objects = [context executeFetchRequest:fetchRequest error:&error];
|
||||
if (!objects) {
|
||||
err( @"Failed to fetch %@ objects: %@", entity, [error fullDescription] );
|
||||
MPError( error, @"Failed to fetch %@ objects.", entity );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -311,7 +335,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
|
||||
- (void)migrateStore {
|
||||
|
||||
MPStoreMigrationLevel migrationLevel = (signed)[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
|
||||
MPStoreMigrationLevel migrationLevel = (MPStoreMigrationLevel)
|
||||
[[NSUserDefaults standardUserDefaults] integerForKey:MPMigrationLevelLocalStoreKey];
|
||||
if (migrationLevel >= MPStoreMigrationLevelCurrent)
|
||||
// Local store up-to-date.
|
||||
return;
|
||||
@@ -353,8 +378,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSError *error = nil;
|
||||
if (![NSPersistentStore migrateStore:oldLocalStoreURL withOptions:@{ STORE_OPTIONS }
|
||||
toStore:newLocalStoreURL withOptions:@{ STORE_OPTIONS }
|
||||
model:nil error:&error]) {
|
||||
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] );
|
||||
error:&error]) {
|
||||
MPError( error, @"Couldn't migrate the old store to the new location." );
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -400,14 +425,52 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
NSMigratePersistentStoresAutomaticallyOption: @YES,
|
||||
NSInferMappingModelAutomaticallyOption : @YES,
|
||||
STORE_OPTIONS
|
||||
} model:nil error:&error]) {
|
||||
err( @"Couldn't migrate the old store to the new location: %@", [error fullDescription] );
|
||||
} error:&error]) {
|
||||
MPError( error, @"Couldn't migrate the old store to the new location." );
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//- (BOOL)migrateV3LocalStore {
|
||||
//
|
||||
// inf( @"Migrating V3 local store" );
|
||||
// NSURL *localStoreURL = [self localStoreURL];
|
||||
// if (![[NSFileManager defaultManager] fileExistsAtPath:localStoreURL.path isDirectory:NULL]) {
|
||||
// inf( @"No V3 local store to migrate." );
|
||||
// return YES;
|
||||
// }
|
||||
//
|
||||
// NSError *error = nil;
|
||||
// NSDictionary<NSString *, id> *metadata = [NSPersistentStore metadataForPersistentStoreWithURL:localStoreURL error:&error];
|
||||
// if (!metadata) {
|
||||
// MPError( error, @"Couldn't inspect metadata for store: %@", localStoreURL );
|
||||
// return NO;
|
||||
// }
|
||||
// NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:
|
||||
// [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:metadata]];
|
||||
// if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil
|
||||
// URL:localStoreURL options:@{ STORE_OPTIONS }
|
||||
// error:&error]) {
|
||||
// MPError( error, @"Couldn't open V3 local store to migrate." );
|
||||
// return NO;
|
||||
// }
|
||||
//
|
||||
// NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
||||
// [context performBlockAndWait:^{
|
||||
// context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
|
||||
// context.persistentStoreCoordinator = coordinator;
|
||||
// for (MPStoredSiteEntity *storedSite in [[MPStoredSiteEntity fetchRequest] execute:&error]) {
|
||||
// id contentObject = [storedSite valueForKey:@"contentObject"];
|
||||
// if ([contentObject isKindOfClass:[NSData class]])
|
||||
// storedSite.contentObject = contentObject;
|
||||
// }
|
||||
// }];
|
||||
//
|
||||
// return YES;
|
||||
//}
|
||||
|
||||
#pragma mark - Utilities
|
||||
|
||||
- (void)addSiteNamed:(NSString *)siteName completion:(void ( ^ )(MPSiteEntity *site, NSManagedObjectContext *context))completion {
|
||||
@@ -436,10 +499,6 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
site.lastUsed = [NSDate date];
|
||||
site.algorithm = algorithm;
|
||||
|
||||
NSError *error = nil;
|
||||
if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
|
||||
err( @"Failed to obtain a permanent object ID after creating new site: %@", [error fullDescription] );
|
||||
|
||||
[context saveToStore];
|
||||
|
||||
completion( site, context );
|
||||
@@ -468,18 +527,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
newSite.algorithm = site.algorithm;
|
||||
newSite.loginName = site.loginName;
|
||||
|
||||
NSError *error = nil;
|
||||
if (![context obtainPermanentIDsForObjects:@[ newSite ] error:&error])
|
||||
err( @"Failed to obtain a permanent object ID after changing object type: %@", [error fullDescription] );
|
||||
|
||||
[context deleteObject:site];
|
||||
[context saveToStore];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.permanentObjectID];
|
||||
site = newSite;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.objectID];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPSiteUpdatedNotification object:site.permanentObjectID];
|
||||
return site;
|
||||
}
|
||||
|
||||
@@ -516,7 +571,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
initWithPattern:@"^#[[:space:]]*([^:]+): (.*)"
|
||||
options:(NSRegularExpressionOptions)0 error:&error];
|
||||
if (error) {
|
||||
err( @"Error loading the header pattern: %@", [error fullDescription] );
|
||||
MPError( error, @"Error loading the header pattern." );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
@@ -530,19 +585,20 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
options:(NSRegularExpressionOptions)0 error:&error]
|
||||
];
|
||||
if (error) {
|
||||
err( @"Error loading the site patterns: %@", [error fullDescription] );
|
||||
MPError( error, @"Error loading the site patterns." );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse import data.
|
||||
inf( @"Importing sites." );
|
||||
__block MPUserEntity *user = nil;
|
||||
id<MPAlgorithm> importAlgorithm = nil;
|
||||
NSUInteger importFormat = 0;
|
||||
__block MPUserEntity *user = nil;
|
||||
NSUInteger importAvatar = NSNotFound;
|
||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
||||
NSData *importKeyID = nil;
|
||||
NSString *importBundleVersion = nil, *importUserName = nil;
|
||||
id<MPAlgorithm> importAlgorithm = nil;
|
||||
MPSiteType importDefaultType = (MPSiteType)0;
|
||||
BOOL headerStarted = NO, headerEnded = NO, clearText = NO;
|
||||
NSArray *importedSiteLines = [importedSitesString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||
NSMutableSet *sitesToDelete = [NSMutableSet set];
|
||||
@@ -573,14 +629,22 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
range:NSMakeRange( 0, [importedSiteLine length] )] lastObject];
|
||||
NSString *headerName = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:1]];
|
||||
NSString *headerValue = [importedSiteLine substringWithRange:[headerSites rangeAtIndex:2]];
|
||||
if ([headerName isEqualToString:@"User Name"]) {
|
||||
|
||||
if ([headerName isEqualToString:@"Format"]) {
|
||||
importFormat = (NSUInteger)[headerValue integerValue];
|
||||
if (importFormat >= [sitePatterns count]) {
|
||||
err( @"Unsupported import format: %lu", (unsigned long)importFormat );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
if (([headerName isEqualToString:@"User Name"] || [headerName isEqualToString:@"Full Name"]) && !importUserName) {
|
||||
importUserName = headerValue;
|
||||
|
||||
NSFetchRequest *userFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
|
||||
userFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", importUserName];
|
||||
NSArray *users = [context executeFetchRequest:userFetchRequest error:&error];
|
||||
if (!users) {
|
||||
err( @"While looking for user: %@, error: %@", importUserName, [error fullDescription] );
|
||||
MPError( error, @"While looking for user: %@.", importUserName );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([users count] > 1) {
|
||||
@@ -591,21 +655,18 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
user = [users lastObject];
|
||||
dbg( @"Existing user? %@", [user debugDescription] );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Avatar"])
|
||||
importAvatar = (NSUInteger)[headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Key ID"])
|
||||
importKeyID = [headerValue decodeHex];
|
||||
if ([headerName isEqualToString:@"Version"]) {
|
||||
importBundleVersion = headerValue;
|
||||
importAlgorithm = MPAlgorithmDefaultForBundleVersion( importBundleVersion );
|
||||
}
|
||||
if ([headerName isEqualToString:@"Format"]) {
|
||||
importFormat = (NSUInteger)[headerValue integerValue];
|
||||
if (importFormat >= [sitePatterns count]) {
|
||||
err( @"Unsupported import format: %lu", (unsigned long)importFormat );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
}
|
||||
if ([headerName isEqualToString:@"Avatar"])
|
||||
importAvatar = (NSUInteger)[headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Algorithm"])
|
||||
importAlgorithm = MPAlgorithmForVersion( (MPAlgorithmVersion)[headerValue integerValue] );
|
||||
if ([headerName isEqualToString:@"Default Type"])
|
||||
importDefaultType = (MPSiteType)[headerValue integerValue];
|
||||
if ([headerName isEqualToString:@"Passwords"]) {
|
||||
if ([headerValue isEqualToString:@"VISIBLE"])
|
||||
clearText = YES;
|
||||
@@ -667,7 +728,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
siteFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@ AND user == %@", siteName, user];
|
||||
NSArray *existingSites = [context executeFetchRequest:siteFetchRequest error:&error];
|
||||
if (!existingSites) {
|
||||
err( @"Lookup of existing sites failed for site: %@, user: %@, error: %@", siteName, user.userID, [error fullDescription] );
|
||||
MPError( error, @"Lookup of existing sites failed for site: %@, user: %@.", siteName, user.userID );
|
||||
return MPImportResultInternalError;
|
||||
}
|
||||
if ([existingSites count]) {
|
||||
@@ -709,6 +770,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
if (user) {
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
if (importDefaultType)
|
||||
user.defaultType = importDefaultType;
|
||||
dbg( @"Updating User: %@", [user debugDescription] );
|
||||
}
|
||||
else {
|
||||
@@ -716,6 +779,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
user.name = importUserName;
|
||||
user.algorithm = MPAlgorithmDefault;
|
||||
user.keyID = [userKey keyIDForAlgorithm:user.algorithm];
|
||||
user.defaultType = importDefaultType?: user.algorithm.defaultType;
|
||||
if (importAvatar != NSNotFound)
|
||||
user.avatar = importAvatar;
|
||||
dbg( @"Created User: %@", [user debugDescription] );
|
||||
@@ -785,16 +849,16 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
|
||||
[export appendFormat:@"# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n"];
|
||||
[export appendFormat:@"# \n"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"# Format: 1\n"];
|
||||
[export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]];
|
||||
[export appendFormat:@"# User Name: %@\n", activeUser.name];
|
||||
[export appendFormat:@"# Full Name: %@\n", activeUser.name];
|
||||
[export appendFormat:@"# Avatar: %lu\n", (unsigned long)activeUser.avatar];
|
||||
[export appendFormat:@"# Key ID: %@\n", [activeUser.keyID encodeHex]];
|
||||
[export appendFormat:@"# Date: %@\n", [[NSDateFormatter rfc3339DateFormatter] stringFromDate:[NSDate date]]];
|
||||
[export appendFormat:@"# Version: %@\n", [PearlInfoPlist get].CFBundleVersion];
|
||||
[export appendFormat:@"# Format: 1\n"];
|
||||
if (revealPasswords)
|
||||
[export appendFormat:@"# Passwords: VISIBLE\n"];
|
||||
else
|
||||
[export appendFormat:@"# Passwords: PROTECTED\n"];
|
||||
[export appendFormat:@"# Algorithm: %d\n", activeUser.algorithm.version];
|
||||
[export appendFormat:@"# Default Type: %d\n", activeUser.defaultType];
|
||||
[export appendFormat:@"# Passwords: %@\n", revealPasswords? @"VISIBLE": @"PROTECTED"];
|
||||
[export appendFormat:@"##\n"];
|
||||
[export appendFormat:@"#\n"];
|
||||
[export appendFormat:@"# Last Times Password Login\t Site\tSite\n"];
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
[self.defaults registerDefaults:@{
|
||||
NSStringFromSelector( @selector( askForReviews ) ) : @YES,
|
||||
|
||||
NSStringFromSelector( @selector( sendInfo ) ) : @NO,
|
||||
NSStringFromSelector( @selector( sendInfo ) ) : @YES,
|
||||
NSStringFromSelector( @selector( rememberLogin ) ) : @NO,
|
||||
NSStringFromSelector( @selector( hidePasswords ) ) : @NO,
|
||||
NSStringFromSelector( @selector( checkInconsistency ) ): @NO,
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPStoredSiteEntity.h"
|
||||
#import "MPGeneratedSiteEntity.h"
|
||||
#import "MPUserEntity.h"
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
#import "MPStoredSiteEntity+CoreDataClass.h"
|
||||
#import "MPGeneratedSiteEntity+CoreDataClass.h"
|
||||
#import "MPUserEntity+CoreDataClass.h"
|
||||
#import "MPAlgorithm.h"
|
||||
#import "MPFixable.h"
|
||||
|
||||
@@ -22,6 +22,12 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface NSManagedObject(MP)
|
||||
|
||||
- (NSManagedObjectID *)permanentObjectID;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPSiteQuestionEntity(MP)
|
||||
|
||||
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key;
|
||||
|
||||
@@ -16,25 +16,41 @@
|
||||
- (BOOL)saveToStore {
|
||||
|
||||
__block BOOL success = YES;
|
||||
if ([self hasChanges]) {
|
||||
if ([self hasChanges])
|
||||
[self performBlockAndWait:^{
|
||||
@try {
|
||||
NSError *error = nil;
|
||||
if (!(success = [self save:&error]))
|
||||
err( @"While saving: %@", [error fullDescription] );
|
||||
MPError( error, @"While saving." );
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
success = NO;
|
||||
err( @"While saving: %@", [exception fullDescription] );
|
||||
err( @"While saving.\n%@", [exception fullDescription] );
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
return success && (!self.parentContext || [self.parentContext saveToStore]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSManagedObject(MP)
|
||||
|
||||
- (NSManagedObjectID *)permanentObjectID {
|
||||
|
||||
NSManagedObjectID *objectID = self.objectID;
|
||||
if ([objectID isTemporaryID]) {
|
||||
NSError *error = nil;
|
||||
if (![self.managedObjectContext obtainPermanentIDsForObjects:@[ self ] error:&error])
|
||||
MPError( error, @"Failed to obtain permanent object ID." );
|
||||
objectID = self.objectID;
|
||||
}
|
||||
|
||||
return objectID.isTemporaryID? nil: objectID;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPSiteQuestionEntity(MP)
|
||||
|
||||
- (NSString *)resolveQuestionAnswerUsingKey:(MPKey *)key {
|
||||
@@ -237,8 +253,8 @@
|
||||
// Invalid self.user.defaultType
|
||||
result = MPApplyFix( result, ^MPFixableResult {
|
||||
wrn( @"Invalid type for: %@ of %@, type: %ld. Will use %ld instead.",
|
||||
self.name, self.user.name, (long)self.type, (long)MPSiteTypeGeneratedLong );
|
||||
self.type = MPSiteTypeGeneratedLong;
|
||||
self.name, self.user.name, (long)self.type, (long)[self.algorithm defaultType] );
|
||||
self.type = [self.algorithm defaultType];
|
||||
return MPFixableResultProblemsFixed;
|
||||
} );
|
||||
if (![self isKindOfClass:[self.algorithm classOfType:self.type]])
|
||||
@@ -330,7 +346,7 @@
|
||||
|
||||
- (MPSiteType)defaultType {
|
||||
|
||||
return (MPSiteType)[self.defaultType_ unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
|
||||
return (MPSiteType)[self.defaultType_ unsignedIntegerValue]?: self.algorithm.defaultType;
|
||||
}
|
||||
|
||||
- (void)setDefaultType:(MPSiteType)aDefaultType {
|
||||
|
||||
20
platform-darwin/Source/MPGeneratedSiteEntity+CoreDataClass.h
Normal file
20
platform-darwin/Source/MPGeneratedSiteEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPGeneratedSiteEntity : MPSiteEntity
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPGeneratedSiteEntity+CoreDataProperties.h"
|
||||
13
platform-darwin/Source/MPGeneratedSiteEntity+CoreDataClass.m
Normal file
13
platform-darwin/Source/MPGeneratedSiteEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGeneratedSiteEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPGeneratedSiteEntity
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGeneratedSiteEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPGeneratedSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPGeneratedSiteEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, copy) NSNumber *counter_;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGeneratedSiteEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPGeneratedSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPGeneratedSiteEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPGeneratedSiteEntity"];
|
||||
}
|
||||
|
||||
@dynamic counter_;
|
||||
|
||||
@end
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
@interface MPGeneratedSiteEntity : MPSiteEntity
|
||||
|
||||
@property(nonatomic, retain) NSNumber *counter_;
|
||||
|
||||
@end
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// MPGeneratedSiteEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPGeneratedSiteEntity.h"
|
||||
|
||||
@implementation MPGeneratedSiteEntity
|
||||
|
||||
@dynamic counter_;
|
||||
|
||||
@end
|
||||
@@ -28,12 +28,12 @@ typedef NS_ENUM( NSUInteger, MPKeyOrigin ) {
|
||||
|
||||
@interface MPKey : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString *fullName;
|
||||
@property(nonatomic, readonly) MPKeyOrigin origin;
|
||||
@property(nonatomic, readonly, copy) NSString *fullName;
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
||||
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin;
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyResolver:(NSData *( ^ )(id<MPAlgorithm>))keyResolver
|
||||
keyOrigin:(MPKeyOrigin)origin;
|
||||
|
||||
- (NSData *)keyIDForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm;
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
@interface MPKey()
|
||||
|
||||
@property(nonatomic) NSString *fullName;
|
||||
@property(nonatomic) MPKeyOrigin origin;
|
||||
@property(nonatomic) NSString *masterPassword;
|
||||
@property(nonatomic, copy) NSString *fullName;
|
||||
@property(nonatomic, copy) NSData *( ^keyResolver )(id<MPAlgorithm>);
|
||||
|
||||
@end
|
||||
|
||||
@@ -31,25 +31,22 @@
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
|
||||
|
||||
return [self initForFullName:fullName withKeyResolver:^NSData *(id<MPAlgorithm> algorithm) {
|
||||
return [algorithm keyDataForFullName:self.fullName withMasterPassword:masterPassword];
|
||||
} keyOrigin:MPKeyOriginMasterPassword];
|
||||
}
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyResolver:(NSData *( ^ )(id<MPAlgorithm>))keyResolver
|
||||
keyOrigin:(MPKeyOrigin)origin {
|
||||
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
_keyCache = [NSCache new];
|
||||
self.fullName = fullName;
|
||||
self.origin = MPKeyOriginMasterPassword;
|
||||
self.masterPassword = masterPassword;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData
|
||||
forAlgorithm:(id<MPAlgorithm>)algorithm keyOrigin:(MPKeyOrigin)origin {
|
||||
|
||||
if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
|
||||
return nil;
|
||||
|
||||
self.origin = origin;
|
||||
[_keyCache setObject:keyData forKey:algorithm];
|
||||
self.fullName = fullName;
|
||||
self.keyResolver = keyResolver;
|
||||
|
||||
return self;
|
||||
}
|
||||
@@ -61,15 +58,17 @@
|
||||
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm {
|
||||
|
||||
NSData *keyData = [_keyCache objectForKey:algorithm];
|
||||
if (keyData)
|
||||
@synchronized (self) {
|
||||
NSData *keyData = [_keyCache objectForKey:algorithm];
|
||||
if (keyData)
|
||||
return keyData;
|
||||
|
||||
keyData = self.keyResolver( algorithm );
|
||||
if (keyData)
|
||||
[_keyCache setObject:keyData forKey:algorithm];
|
||||
|
||||
return keyData;
|
||||
|
||||
keyData = [algorithm keyDataForFullName:self.fullName withMasterPassword:self.masterPassword];
|
||||
if (keyData)
|
||||
[_keyCache setObject:keyData forKey:algorithm];
|
||||
|
||||
return keyData;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)keyDataForAlgorithm:(id<MPAlgorithm>)algorithm trimmedLength:(NSUInteger)subKeyLength {
|
||||
@@ -80,7 +79,7 @@
|
||||
|
||||
- (BOOL)isEqualToKey:(MPKey *)key {
|
||||
|
||||
return [self.fullName isEqualToString:key.fullName] && [self.masterPassword isEqualToString:self.masterPassword];
|
||||
return [[self keyIDForAlgorithm:MPAlgorithmDefault] isEqualToData:[key keyIDForAlgorithm:MPAlgorithmDefault]];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
|
||||
22
platform-darwin/Source/MPSiteEntity+CoreDataClass.h
Normal file
22
platform-darwin/Source/MPSiteEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPSiteEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteQuestionEntity, MPUserEntity, NSObject;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPSiteEntity : NSManagedObject
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPSiteEntity+CoreDataProperties.h"
|
||||
16
platform-darwin/Source/MPSiteEntity+CoreDataClass.m
Normal file
16
platform-darwin/Source/MPSiteEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// MPSiteEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
|
||||
#import "MPUserEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPSiteEntity
|
||||
|
||||
@end
|
||||
48
platform-darwin/Source/MPSiteEntity+CoreDataProperties.h
Normal file
48
platform-darwin/Source/MPSiteEntity+CoreDataProperties.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// MPSiteEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPSiteEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, retain) NSObject *content;
|
||||
@property (nullable, nonatomic, copy) NSDate *lastUsed;
|
||||
@property (nullable, nonatomic, copy) NSNumber *loginGenerated_;
|
||||
@property (nullable, nonatomic, copy) NSString *loginName;
|
||||
@property (nullable, nonatomic, copy) NSString *name;
|
||||
@property (nullable, nonatomic, copy) NSNumber *requiresExplicitMigration_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *type_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *uses_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *version_;
|
||||
@property (nullable, nonatomic, copy) NSString *url;
|
||||
@property (nullable, nonatomic, retain) NSOrderedSet<MPSiteQuestionEntity *> *questions;
|
||||
@property (nullable, nonatomic, retain) MPUserEntity *user;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPSiteEntity (CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)insertObject:(MPSiteQuestionEntity *)value inQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)removeObjectFromQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)insertQuestions:(NSArray<MPSiteQuestionEntity *> *)value atIndexes:(NSIndexSet *)indexes;
|
||||
- (void)removeQuestionsAtIndexes:(NSIndexSet *)indexes;
|
||||
- (void)replaceObjectInQuestionsAtIndex:(NSUInteger)idx withObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)replaceQuestionsAtIndexes:(NSIndexSet *)indexes withQuestions:(NSArray<MPSiteQuestionEntity *> *)values;
|
||||
- (void)addQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)removeQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)addQuestions:(NSOrderedSet<MPSiteQuestionEntity *> *)values;
|
||||
- (void)removeQuestions:(NSOrderedSet<MPSiteQuestionEntity *> *)values;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
30
platform-darwin/Source/MPSiteEntity+CoreDataProperties.m
Normal file
30
platform-darwin/Source/MPSiteEntity+CoreDataProperties.m
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// MPSiteEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPSiteEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPSiteEntity"];
|
||||
}
|
||||
|
||||
@dynamic content;
|
||||
@dynamic lastUsed;
|
||||
@dynamic loginGenerated_;
|
||||
@dynamic loginName;
|
||||
@dynamic name;
|
||||
@dynamic requiresExplicitMigration_;
|
||||
@dynamic type_;
|
||||
@dynamic uses_;
|
||||
@dynamic version_;
|
||||
@dynamic url;
|
||||
@dynamic questions;
|
||||
@dynamic user;
|
||||
|
||||
@end
|
||||
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// MPSiteEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteQuestionEntity, MPUserEntity;
|
||||
|
||||
@interface MPSiteEntity : NSManagedObject
|
||||
|
||||
//@property (nonatomic, retain) id content; // Hide here, reveal in MPStoredSiteEntity
|
||||
@property(nonatomic, retain) NSDate *lastUsed;
|
||||
@property(nonatomic, retain) NSNumber *loginGenerated_;
|
||||
@property(nonatomic, retain) NSString *loginName;
|
||||
@property(nonatomic, retain) NSString *name;
|
||||
@property(nonatomic, retain) NSNumber *requiresExplicitMigration_;
|
||||
@property(nonatomic, retain) NSNumber *type_;
|
||||
@property(nonatomic, retain) NSNumber *uses_;
|
||||
@property(nonatomic, retain) NSNumber *version_;
|
||||
@property(nonatomic, retain) NSOrderedSet *questions;
|
||||
@property(nonatomic, retain) MPUserEntity *user;
|
||||
@end
|
||||
|
||||
@interface MPSiteEntity(CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)insertObject:(MPSiteQuestionEntity *)value inQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)removeObjectFromQuestionsAtIndex:(NSUInteger)idx;
|
||||
- (void)insertQuestions:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
|
||||
- (void)removeQuestionsAtIndexes:(NSIndexSet *)indexes;
|
||||
- (void)replaceObjectInQuestionsAtIndex:(NSUInteger)idx withObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)replaceQuestionsAtIndexes:(NSIndexSet *)indexes withQuestions:(NSArray *)values;
|
||||
- (void)addQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)removeQuestionsObject:(MPSiteQuestionEntity *)value;
|
||||
- (void)addQuestions:(NSOrderedSet *)values;
|
||||
- (void)removeQuestions:(NSOrderedSet *)values;
|
||||
@end
|
||||
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// MPSiteEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteEntity.h"
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPUserEntity.h"
|
||||
|
||||
@implementation MPSiteEntity
|
||||
|
||||
//@dynamic content;
|
||||
@dynamic lastUsed;
|
||||
@dynamic loginGenerated_;
|
||||
@dynamic loginName;
|
||||
@dynamic name;
|
||||
@dynamic requiresExplicitMigration_;
|
||||
@dynamic type_;
|
||||
@dynamic uses_;
|
||||
@dynamic version_;
|
||||
@dynamic questions;
|
||||
@dynamic user;
|
||||
|
||||
@end
|
||||
22
platform-darwin/Source/MPSiteQuestionEntity+CoreDataClass.h
Normal file
22
platform-darwin/Source/MPSiteQuestionEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPSiteQuestionEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPSiteQuestionEntity : NSManagedObject
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPSiteQuestionEntity+CoreDataProperties.h"
|
||||
14
platform-darwin/Source/MPSiteQuestionEntity+CoreDataClass.m
Normal file
14
platform-darwin/Source/MPSiteQuestionEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// MPSiteQuestionEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPSiteQuestionEntity
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// MPSiteQuestionEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPSiteQuestionEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPSiteQuestionEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, copy) NSString *keyword;
|
||||
@property (nullable, nonatomic, retain) MPSiteEntity *site;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// MPSiteQuestionEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteQuestionEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPSiteQuestionEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPSiteQuestionEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPSiteQuestionEntity"];
|
||||
}
|
||||
|
||||
@dynamic keyword;
|
||||
@dynamic site;
|
||||
|
||||
@end
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// MPSiteQuestionEntity.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-27.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
@interface MPSiteQuestionEntity : NSManagedObject
|
||||
|
||||
@property(nonatomic, retain) NSString *keyword;
|
||||
@property(nonatomic, retain) MPSiteEntity *site;
|
||||
|
||||
@end
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// MPSiteQuestionEntity.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-27.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
@implementation MPSiteQuestionEntity
|
||||
|
||||
@dynamic keyword;
|
||||
@dynamic site;
|
||||
|
||||
@end
|
||||
22
platform-darwin/Source/MPStoredSiteEntity+CoreDataClass.h
Normal file
22
platform-darwin/Source/MPStoredSiteEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPStoredSiteEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
@class NSObject;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPStoredSiteEntity : MPSiteEntity
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPStoredSiteEntity+CoreDataProperties.h"
|
||||
13
platform-darwin/Source/MPStoredSiteEntity+CoreDataClass.m
Normal file
13
platform-darwin/Source/MPStoredSiteEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// MPStoredSiteEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPStoredSiteEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPStoredSiteEntity
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPStoredSiteEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-05-01.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPStoredSiteEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPStoredSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPStoredSiteEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, retain) NSData *contentObject;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// MPStoredSiteEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-05-01.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPStoredSiteEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPStoredSiteEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPStoredSiteEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPStoredSiteEntity"];
|
||||
}
|
||||
|
||||
@dynamic contentObject;
|
||||
|
||||
@end
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// MPStoredSiteEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
@interface MPStoredSiteEntity : MPSiteEntity
|
||||
|
||||
@property(nonatomic, retain) NSData *contentObject;
|
||||
|
||||
@end
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// MPStoredSiteEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPStoredSiteEntity.h"
|
||||
|
||||
@implementation MPStoredSiteEntity
|
||||
|
||||
@dynamic contentObject;
|
||||
|
||||
@end
|
||||
@@ -6,8 +6,12 @@
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Crashlytics/Crashlytics.h>
|
||||
#import <Crashlytics/Answers.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
extern NSString *const MPErrorDomain;
|
||||
extern NSInteger const MPErrorHangCode;
|
||||
|
||||
extern NSString *const MPSignedInNotification;
|
||||
extern NSString *const MPSignedOutNotification;
|
||||
@@ -19,4 +23,21 @@ extern NSString *const MPFoundInconsistenciesNotification;
|
||||
|
||||
extern NSString *const MPSitesImportedNotificationUserKey;
|
||||
extern NSString *const MPInconsistenciesFixResultUserKey;
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#ifdef CRASHLYTICS
|
||||
#define MPError(error_, message, ...) ({ \
|
||||
err( message @"%@%@", ##__VA_ARGS__, error_? @"\n": @"", [error_ fullDescription]?: @"" ); \
|
||||
\
|
||||
if ([[MPConfig get].sendInfo boolValue]) { \
|
||||
[[Crashlytics sharedInstance] recordError:error_ withAdditionalUserInfo:@{ \
|
||||
@"location": strf( @"%@:%d %@", @(basename((char *)__FILE__)), __LINE__, NSStringFromSelector(_cmd) ), \
|
||||
}]; \
|
||||
} \
|
||||
})
|
||||
#else
|
||||
#define MPError(error_, ...) ({ \
|
||||
err( message @"%@%@", ##__VA_ARGS__, error_? @"\n": @"", [error_ fullDescription]?: @"" ); \
|
||||
})
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import "MPTypes.h"
|
||||
|
||||
NSString *const MPErrorDomain = @"MPErrorDomain";
|
||||
NSInteger const MPErrorHangCode = 1;
|
||||
|
||||
NSString *const MPSignedInNotification = @"MPSignedInNotification";
|
||||
NSString *const MPSignedOutNotification = @"MPSignedOutNotification";
|
||||
|
||||
22
platform-darwin/Source/MPUserEntity+CoreDataClass.h
Normal file
22
platform-darwin/Source/MPUserEntity+CoreDataClass.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// MPUserEntity+CoreDataClass.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPUserEntity : NSManagedObject
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#import "MPUserEntity+CoreDataProperties.h"
|
||||
14
platform-darwin/Source/MPUserEntity+CoreDataClass.m
Normal file
14
platform-darwin/Source/MPUserEntity+CoreDataClass.m
Normal file
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// MPUserEntity+CoreDataClass.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPUserEntity+CoreDataClass.h"
|
||||
#import "MPSiteEntity+CoreDataClass.h"
|
||||
|
||||
@implementation MPUserEntity
|
||||
|
||||
@end
|
||||
39
platform-darwin/Source/MPUserEntity+CoreDataProperties.h
Normal file
39
platform-darwin/Source/MPUserEntity+CoreDataProperties.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// MPUserEntity+CoreDataProperties.h
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPUserEntity+CoreDataClass.h"
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MPUserEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPUserEntity *> *)fetchRequest;
|
||||
|
||||
@property (nullable, nonatomic, copy) NSNumber *avatar_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *defaultType_;
|
||||
@property (nullable, nonatomic, retain) NSData *keyID;
|
||||
@property (nullable, nonatomic, copy) NSDate *lastUsed;
|
||||
@property (nullable, nonatomic, copy) NSString *name;
|
||||
@property (nullable, nonatomic, copy) NSNumber *saveKey_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *touchID_;
|
||||
@property (nullable, nonatomic, copy) NSNumber *version_;
|
||||
@property (nullable, nonatomic, retain) NSSet<MPSiteEntity *> *sites;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPUserEntity (CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)addSitesObject:(MPSiteEntity *)value;
|
||||
- (void)removeSitesObject:(MPSiteEntity *)value;
|
||||
- (void)addSites:(NSSet<MPSiteEntity *> *)values;
|
||||
- (void)removeSites:(NSSet<MPSiteEntity *> *)values;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
27
platform-darwin/Source/MPUserEntity+CoreDataProperties.m
Normal file
27
platform-darwin/Source/MPUserEntity+CoreDataProperties.m
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// MPUserEntity+CoreDataProperties.m
|
||||
// MasterPassword-iOS
|
||||
//
|
||||
// Created by Maarten Billemont on 2017-04-30.
|
||||
// Copyright © 2017 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPUserEntity+CoreDataProperties.h"
|
||||
|
||||
@implementation MPUserEntity (CoreDataProperties)
|
||||
|
||||
+ (NSFetchRequest<MPUserEntity *> *)fetchRequest {
|
||||
return [[NSFetchRequest alloc] initWithEntityName:@"MPUserEntity"];
|
||||
}
|
||||
|
||||
@dynamic avatar_;
|
||||
@dynamic defaultType_;
|
||||
@dynamic keyID;
|
||||
@dynamic lastUsed;
|
||||
@dynamic name;
|
||||
@dynamic saveKey_;
|
||||
@dynamic touchID_;
|
||||
@dynamic version_;
|
||||
@dynamic sites;
|
||||
|
||||
@end
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// MPUserEntity.h
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class MPSiteEntity;
|
||||
|
||||
@interface MPUserEntity : NSManagedObject
|
||||
|
||||
@property(nonatomic, retain) NSNumber *avatar_;
|
||||
@property(nonatomic, retain) NSNumber *defaultType_;
|
||||
@property(nonatomic, retain) NSData *keyID;
|
||||
@property(nonatomic, retain) NSDate *lastUsed;
|
||||
@property(nonatomic, retain) NSString *name;
|
||||
@property(nonatomic, retain) NSNumber *saveKey_;
|
||||
@property(nonatomic, retain) NSNumber *touchID_;
|
||||
@property(nonatomic, retain) NSNumber *version_;
|
||||
@property(nonatomic, retain) NSSet *sites;
|
||||
@end
|
||||
|
||||
@interface MPUserEntity(CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)addSitesObject:(MPSiteEntity *)value;
|
||||
- (void)removeSitesObject:(MPSiteEntity *)value;
|
||||
- (void)addSites:(NSSet *)values;
|
||||
- (void)removeSites:(NSSet *)values;
|
||||
|
||||
@end
|
||||
@@ -1,24 +0,0 @@
|
||||
//
|
||||
// MPUserEntity.m
|
||||
// MasterPassword-Mac
|
||||
//
|
||||
// Created by Maarten Billemont on 2014-09-21.
|
||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MPUserEntity.h"
|
||||
#import "MPSiteEntity.h"
|
||||
|
||||
@implementation MPUserEntity
|
||||
|
||||
@dynamic avatar_;
|
||||
@dynamic defaultType_;
|
||||
@dynamic keyID;
|
||||
@dynamic lastUsed;
|
||||
@dynamic name;
|
||||
@dynamic saveKey_;
|
||||
@dynamic touchID_;
|
||||
@dynamic version_;
|
||||
@dynamic sites;
|
||||
|
||||
@end
|
||||
@@ -28,10 +28,10 @@
|
||||
|
||||
[super windowDidLoad];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
|
||||
queue:nil usingBlock:^(NSNotification *note) {
|
||||
[MPMacAppDelegate get].initialWindowController = nil;
|
||||
}];
|
||||
PearlAddNotificationObserver( NSWindowWillCloseNotification, self.window, nil, ^(id host, NSNotification *note) {
|
||||
PearlRemoveNotificationObserversFrom( host );
|
||||
[MPMacAppDelegate get].initialWindowController = nil;
|
||||
} );
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
@@ -72,13 +72,12 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
|
||||
if ([crashlyticsAPIKey length]) {
|
||||
inf(@"Initializing Crashlytics");
|
||||
#if defined (DEBUG) || defined (ADHOC)
|
||||
#if DEBUG
|
||||
[Crashlytics sharedInstance].debugMode = YES;
|
||||
#endif
|
||||
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
|
||||
[[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
|
||||
[[Crashlytics sharedInstance] setUserName:@"Anonymous"];
|
||||
[[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
|
||||
[Crashlytics startWithAPIKey:crashlyticsAPIKey];
|
||||
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
|
||||
PearlLogLevel level = PearlLogLevelInfo;
|
||||
@@ -172,17 +171,17 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||
// Save changes in the application's managed object context before the application terminates.
|
||||
|
||||
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
if (!context)
|
||||
NSManagedObjectContext *mainContext = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
if (!mainContext)
|
||||
return NSTerminateNow;
|
||||
|
||||
if (![context commitEditing])
|
||||
if (![mainContext commitEditing])
|
||||
return NSTerminateCancel;
|
||||
|
||||
if (![context hasChanges])
|
||||
if (![mainContext hasChanges])
|
||||
return NSTerminateNow;
|
||||
|
||||
[context saveToStore];
|
||||
[mainContext saveToStore];
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
@@ -272,7 +271,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
|
||||
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
|
||||
if (error)
|
||||
err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
|
||||
MPError( error, @"While reading imported sites from %@.", url );
|
||||
if (!importedSitesData)
|
||||
return;
|
||||
|
||||
@@ -389,20 +388,16 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
return;
|
||||
|
||||
NSString *name = [(NSSecureTextField *)alert.accessoryView stringValue];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *newUser = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass( [MPUserEntity class] )
|
||||
inManagedObjectContext:moc];
|
||||
inManagedObjectContext:context];
|
||||
newUser.name = name;
|
||||
[moc saveToStore];
|
||||
NSError *error = nil;
|
||||
if (![moc obtainPermanentIDsForObjects:@[ newUser ] error:&error])
|
||||
err( @"Failed to obtain permanent object ID for new user: %@", [error fullDescription] );
|
||||
[context saveToStore];
|
||||
[self setActiveUser:newUser];
|
||||
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
[self updateUsers];
|
||||
[self setActiveUser:newUser];
|
||||
PearlMainQueue( ^{
|
||||
[self showPasswordWindow:nil];
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -416,10 +411,10 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
if ([alert runModal] != NSAlertFirstButtonReturn)
|
||||
return;
|
||||
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
[moc deleteObject:[self activeUserInContext:moc]];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
[context deleteObject:[self activeUserInContext:context]];
|
||||
[self setActiveUser:nil];
|
||||
[moc saveToStore];
|
||||
[context saveToStore];
|
||||
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
[self updateUsers];
|
||||
@@ -516,20 +511,23 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
|
||||
NSError *coordinateError = nil;
|
||||
NSString *exportedSites = [self exportSitesRevealPasswords:revealPasswords];
|
||||
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0 error:&coordinateError
|
||||
byAccessor:^(NSURL *newURL) {
|
||||
NSError *writeError = nil;
|
||||
if (![exportedSites writeToURL:newURL atomically:NO
|
||||
encoding:NSUTF8StringEncoding
|
||||
error:&writeError])
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:writeError] runModal];
|
||||
} );
|
||||
}];
|
||||
if (coordinateError)
|
||||
[[[NSFileCoordinator alloc] initWithFilePresenter:nil] coordinateWritingItemAtURL:savePanel.URL options:0
|
||||
error:&coordinateError byAccessor:
|
||||
^(NSURL *newURL) {
|
||||
NSError *writeError = nil;
|
||||
if (![exportedSites writeToURL:newURL atomically:NO encoding:NSUTF8StringEncoding error:&writeError])
|
||||
MPError( writeError, @"Could not write to the export file." );
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:writeError] runModal];
|
||||
} );
|
||||
}];
|
||||
if (coordinateError) {
|
||||
MPError( coordinateError, @"Write access to the export file could not be obtained." );
|
||||
PearlMainQueue( ^{
|
||||
[[NSAlert alertWithError:coordinateError] runModal];
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateUsers {
|
||||
@@ -567,7 +565,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
|
||||
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (!users)
|
||||
err( @"Failed to load users: %@", [error fullDescription] );
|
||||
MPError( error, @"Failed to load users." );
|
||||
|
||||
if (![users count]) {
|
||||
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
|
||||
@@ -579,7 +577,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
|
||||
for (MPUserEntity *user in users) {
|
||||
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""];
|
||||
[userItem setTarget:self];
|
||||
[userItem setRepresentedObject:[user objectID]];
|
||||
[userItem setRepresentedObject:user.permanentObjectID];
|
||||
[[self.usersItem submenu] addItem:userItem];
|
||||
|
||||
if (!mainActiveUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])
|
||||
|
||||
@@ -41,40 +41,38 @@
|
||||
[self replaceFonts:self.window.contentView];
|
||||
prof_rewind( @"replaceFonts" );
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowDidBecomeKeyNotification object:self.window
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
PearlAddNotificationObserver( NSWindowDidBecomeKeyNotification, self.window, [NSOperationQueue mainQueue],
|
||||
^(id host, NSNotification *note) {
|
||||
prof_new( @"didBecomeKey" );
|
||||
[self.window makeKeyAndOrderFront:nil];
|
||||
prof_rewind( @"fadeIn" );
|
||||
[self updateUser];
|
||||
prof_finish( @"updateUser" );
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:self.window
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
} );
|
||||
PearlAddNotificationObserver( NSWindowWillCloseNotification, self.window, [NSOperationQueue mainQueue],
|
||||
^(id host, NSNotification *note) {
|
||||
PearlRemoveNotificationObservers();
|
||||
|
||||
NSWindow *sheet = [self.window attachedSheet];
|
||||
if (sheet)
|
||||
[self.window endSheet:sheet];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillResignActiveNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
} );
|
||||
PearlAddNotificationObserver( NSApplicationWillResignActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(id host, NSNotification *note) {
|
||||
[self.window close];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedInNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[self updateUser];
|
||||
}];
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[self updateUser];
|
||||
}];
|
||||
} );
|
||||
PearlAddNotificationObserver( MPSignedInNotification, nil, [NSOperationQueue mainQueue], ^(id host, NSNotification *note) {
|
||||
[self updateUser];
|
||||
} );
|
||||
PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue], ^(id host, NSNotification *note) {
|
||||
[self updateUser];
|
||||
} );
|
||||
[self observeKeyPath:@"sitesController.selection" withBlock:^(id from, id to, NSKeyValueChange cause, id _self) {
|
||||
prof_new( @"sitesController.selection" );
|
||||
[_self updateSelection];
|
||||
prof_finish( @"updateSelection" );
|
||||
}];
|
||||
prof_rewind( @"observers" );
|
||||
|
||||
NSSearchFieldCell *siteFieldCell = (NSSearchFieldCell *)self.siteField.cell;
|
||||
NSSearchFieldCell *siteFieldCell = self.siteField.cell;
|
||||
siteFieldCell.searchButtonCell = nil;
|
||||
siteFieldCell.cancelButtonCell = nil;
|
||||
|
||||
@@ -162,10 +160,10 @@
|
||||
- (IBAction)doUnlockUser:(id)sender {
|
||||
|
||||
[self.progressView startAnimation:self];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:moc];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserInContext:context];
|
||||
NSString *userName = activeUser.name;
|
||||
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:moc usingMasterPassword:self.masterPassword];
|
||||
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser saveInContext:context usingMasterPassword:self.masterPassword];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
self.masterPassword = nil;
|
||||
@@ -549,15 +547,15 @@
|
||||
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"lastUsed" ascending:NO] ];
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||
queryPattern, queryPattern, [MPMacAppDelegate get].activeUserOID];
|
||||
fetchRequest.predicate =
|
||||
[NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPMacAppDelegate get].activeUserOID];
|
||||
prof_rewind( @"fetchRequest" );
|
||||
|
||||
NSError *error = nil;
|
||||
NSArray *siteResults = [context executeFetchRequest:fetchRequest error:&error];
|
||||
if (!siteResults) {
|
||||
prof_finish( @"executeFetchRequest: %@ // %@", fetchRequest.predicate, [error fullDescription] );
|
||||
err( @"While fetching sites for completion: %@", [error fullDescription] );
|
||||
MPError( error, @"While fetching sites for completion." );
|
||||
return;
|
||||
}
|
||||
prof_rewind( @"executeFetchRequest: %@", fetchRequest.predicate );
|
||||
@@ -635,9 +633,9 @@
|
||||
return;
|
||||
}
|
||||
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
|
||||
[[self.selectedSite entityInContext:moc] use];
|
||||
[moc saveToStore];
|
||||
[MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
[[self.selectedSite entityInContext:context] use];
|
||||
[context saveToStore];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@@ -52,9 +52,9 @@
|
||||
|
||||
- (void)setEntity:(MPSiteEntity *)entity fuzzyGroups:(NSArray *)fuzzyGroups {
|
||||
|
||||
if ([_entityOID isEqual:entity.objectID])
|
||||
if ([_entityOID isEqual:entity.permanentObjectID])
|
||||
return;
|
||||
_entityOID = entity.objectID;
|
||||
_entityOID = entity.permanentObjectID;
|
||||
|
||||
NSString *siteName = entity.name;
|
||||
NSMutableAttributedString *attributedSiteName = [[NSMutableAttributedString alloc] initWithString:siteName];
|
||||
@@ -115,7 +115,7 @@
|
||||
NSError *error;
|
||||
MPSiteEntity *entity = (MPSiteEntity *)[moc existingObjectWithID:_entityOID error:&error];
|
||||
if (!entity)
|
||||
err( @"Couldn't retrieve active site: %@", [error fullDescription] );
|
||||
MPError( error, @"Couldn't retrieve active site." );
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MasterPassword 8.xcdatamodel</string>
|
||||
<string>MasterPassword 9.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12141" systemVersion="16E195" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||
<entity name="MPGeneratedSiteEntity" representedClassName="MPGeneratedSiteEntity" parentEntity="MPSiteEntity" elementID="789304EA-070D-4982-8C20-54EECFC20CB6" syncable="YES">
|
||||
<attribute name="counter_" optional="YES" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPSiteEntity" representedClassName="MPSiteEntity" isAbstract="YES" elementID="58EE245C-6827-4C11-BB7E-5722F2426EC2" syncable="YES">
|
||||
<attribute name="content" optional="YES" transient="YES" attributeType="Transformable" syncable="YES"/>
|
||||
<attribute name="lastUsed" attributeType="Date" usesScalarValueType="NO" indexed="YES" syncable="YES"/>
|
||||
<attribute name="loginGenerated_" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="loginName" optional="YES" attributeType="String" elementID="A1B9F981-D33C-4BFE-9F94-C9D3E1F78E51" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" indexed="YES" syncable="YES"/>
|
||||
<attribute name="requiresExplicitMigration_" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO">
|
||||
<userInfo/>
|
||||
</attribute>
|
||||
<attribute name="type_" attributeType="Integer 16" defaultValueString="17" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="url" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="uses_" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" indexed="YES" syncable="YES"/>
|
||||
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<relationship name="questions" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="MPSiteQuestionEntity" inverseName="site" inverseEntity="MPSiteQuestionEntity" syncable="YES"/>
|
||||
<relationship name="user" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="MPUserEntity" inverseName="sites" inverseEntity="MPUserEntity" elementID="FC8AE32E-7BE3-4FA6-8611-B7DC0DB063EF" syncable="YES"/>
|
||||
<compoundIndexes>
|
||||
<compoundIndex>
|
||||
<index value="name"/>
|
||||
<index value="user"/>
|
||||
</compoundIndex>
|
||||
</compoundIndexes>
|
||||
</entity>
|
||||
<entity name="MPSiteQuestionEntity" representedClassName="MPSiteQuestionEntity" syncable="YES">
|
||||
<attribute name="keyword" attributeType="String" syncable="YES"/>
|
||||
<relationship name="site" maxCount="1" deletionRule="Nullify" destinationEntity="MPSiteEntity" inverseName="questions" inverseEntity="MPSiteEntity" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPStoredSiteEntity" representedClassName="MPStoredSiteEntity" parentEntity="MPSiteEntity" elementID="BEEF1688-0CCD-4699-A86A-4D860FE2CEB8" syncable="YES">
|
||||
<attribute name="contentObject" optional="YES" attributeType="Transformable" customClassName="NSData" storedInTruthFile="YES" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="MPUserEntity" representedClassName="MPUserEntity" syncable="YES">
|
||||
<attribute name="avatar_" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="defaultType_" attributeType="Integer 16" defaultValueString="17" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="keyID" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||
<attribute name="lastUsed" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="saveKey_" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO">
|
||||
<userInfo/>
|
||||
</attribute>
|
||||
<attribute name="touchID_" attributeType="Boolean" defaultValueString="0" usesScalarValueType="NO">
|
||||
<userInfo/>
|
||||
</attribute>
|
||||
<attribute name="version_" attributeType="Integer 16" minValueString="0" defaultValueString="2" usesScalarValueType="NO" syncable="YES"/>
|
||||
<relationship name="sites" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="MPSiteEntity" inverseName="user" inverseEntity="MPSiteEntity" elementID="D18D6772-040E-4CFE-8F32-A34B08E9E9BC" syncable="YES"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="MPGeneratedSiteEntity" positionX="216" positionY="-288" width="128" height="58"/>
|
||||
<element name="MPSiteEntity" positionX="-0" positionY="-286" width="128" height="225"/>
|
||||
<element name="MPSiteQuestionEntity" positionX="-2" positionY="-9" width="128" height="73"/>
|
||||
<element name="MPStoredSiteEntity" positionX="214" positionY="-171" width="128" height="60"/>
|
||||
<element name="MPUserEntity" positionX="-218" positionY="-288" width="128" height="178"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "MPTypeViewController.h"
|
||||
#import "MPSiteQuestionEntity.h"
|
||||
#import "MPSiteQuestionEntity+CoreDataClass.h"
|
||||
|
||||
@interface MPAnswersViewController : UIViewController
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
- (void)setSite:(MPSiteEntity *)site {
|
||||
|
||||
_siteOID = [site objectID];
|
||||
_siteOID = site.permanentObjectID;
|
||||
_multiple = [site.questions count] > 0;
|
||||
[self.tableView reloadData];
|
||||
[self updateAnimated:NO];
|
||||
@@ -161,10 +161,9 @@
|
||||
MPSiteEntity *site = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
|
||||
|
||||
if ([cell isKindOfClass:[MPGlobalAnswersCell class]]) {
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied" ) dismissAfter:2];
|
||||
[UIPasteboard generalPasteboard].string = ((MPGlobalAnswersCell *)cell).answerField.text;
|
||||
}
|
||||
if ([cell isKindOfClass:[MPGlobalAnswersCell class]])
|
||||
[self copyAnswer:((MPGlobalAnswersCell *)cell).answerField.text];
|
||||
|
||||
else if ([cell isKindOfClass:[MPMultipleAnswersCell class]]) {
|
||||
if (!_multiple)
|
||||
[self setMultiple:YES animated:YES];
|
||||
@@ -192,6 +191,7 @@
|
||||
} cancelTitle:@"Cancel" otherTitles:@"Remove Questions", nil];
|
||||
}
|
||||
}
|
||||
|
||||
else if ([cell isKindOfClass:[MPSendAnswersCell class]]) {
|
||||
NSString *body;
|
||||
if (!_multiple) {
|
||||
@@ -215,14 +215,30 @@
|
||||
|
||||
[PearlEMail sendEMailTo:nil fromVC:self subject:strf( @"Master Password security answers for %@", site.name ) body:body];
|
||||
}
|
||||
else if ([cell isKindOfClass:[MPAnswersQuestionCell class]]) {
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied" ) dismissAfter:2];
|
||||
[UIPasteboard generalPasteboard].string = ((MPAnswersQuestionCell *)cell).answerField.text;
|
||||
}
|
||||
|
||||
else if ([cell isKindOfClass:[MPAnswersQuestionCell class]])
|
||||
[self copyAnswer:((MPAnswersQuestionCell *)cell).answerField.text];
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
|
||||
- (void)copyAnswer:(NSString *)answer {
|
||||
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
if ([pasteboard respondsToSelector:@selector( setItems:options: )]) {
|
||||
[pasteboard setItems:@[ @{ UIPasteboardTypeAutomatic: answer } ]
|
||||
options:@{
|
||||
UIPasteboardOptionLocalOnly : @NO,
|
||||
UIPasteboardOptionExpirationDate: [NSDate dateWithTimeIntervalSinceNow:3 * 60]
|
||||
}];
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied (3 min)" ) dismissAfter:2];
|
||||
}
|
||||
else {
|
||||
pasteboard.string = answer;
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Answer Copied" ) dismissAfter:2];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)updateAnimated:(BOOL)animated {
|
||||
@@ -285,8 +301,8 @@
|
||||
|
||||
- (void)setQuestion:(MPSiteQuestionEntity *)question forSite:(MPSiteEntity *)site inVC:(MPAnswersViewController *)answersVC {
|
||||
|
||||
_siteOID = site.objectID;
|
||||
_questionOID = question.objectID;
|
||||
_siteOID = site.permanentObjectID;
|
||||
_questionOID = question.permanentObjectID;
|
||||
_answersVC = answersVC;
|
||||
|
||||
[self updateAnswerForQuestion:question ofSite:site];
|
||||
@@ -317,14 +333,7 @@
|
||||
question.keyword = keyword;
|
||||
|
||||
if ([context saveToStore]) {
|
||||
if ([question.objectID isTemporaryID]) {
|
||||
NSError *error = nil;
|
||||
[context obtainPermanentIDsForObjects:@[ question ] error:&error];
|
||||
if (error)
|
||||
err( @"Failed to obtain permanent object ID: %@", [error fullDescription] );
|
||||
}
|
||||
|
||||
_questionOID = question.objectID;
|
||||
_questionOID = question.permanentObjectID;
|
||||
[self updateAnswerForQuestion:question ofSite:site];
|
||||
|
||||
if (didAddQuestionObject)
|
||||
|
||||
@@ -49,11 +49,10 @@ const long MPAvatarAdd = 10000;
|
||||
|
||||
[super awakeFromNib];
|
||||
|
||||
self.alpha = 0;
|
||||
self.visible = NO;
|
||||
|
||||
self.nameContainer.layer.cornerRadius = 5;
|
||||
|
||||
self.avatarImageView.hidden = NO;
|
||||
self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.size.height / 2;
|
||||
self.avatarImageView.layer.masksToBounds = NO;
|
||||
self.avatarImageView.backgroundColor = [UIColor clearColor];
|
||||
@@ -211,7 +210,7 @@ const long MPAvatarAdd = 10000;
|
||||
[UIView animateWithDuration:animated? 0.5f: 0 delay:0
|
||||
options:UIViewAnimationOptionOverrideInheritedDuration | UIViewAnimationOptionBeginFromCurrentState
|
||||
animations:^{
|
||||
self.alpha = 1;
|
||||
self.visible = YES;
|
||||
|
||||
if (self.newUser) {
|
||||
if (self.mode == MPAvatarModeLowered)
|
||||
@@ -220,6 +219,10 @@ const long MPAvatarAdd = 10000;
|
||||
self.avatar = arc4random() % MPAvatarCount;
|
||||
}
|
||||
|
||||
self.nameContainer.alpha = self.visibility;
|
||||
self.avatarImageView.alpha = self.visibility * 0.7f + 0.3f;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
|
||||
switch (self.mode) {
|
||||
case MPAvatarModeLowered: {
|
||||
[self.avatarSizeConstraint updateConstant:
|
||||
@@ -227,10 +230,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
self.nameContainer.alpha = self.visibility;
|
||||
self.nameContainer.visible = YES;
|
||||
self.nameContainer.backgroundColor = [UIColor clearColor];
|
||||
self.avatarImageView.alpha = self.visibility * 0.7f + 0.3f;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
self.avatarImageView.visible = YES;
|
||||
break;
|
||||
}
|
||||
case MPAvatarModeRaisedButInactive: {
|
||||
@@ -239,10 +241,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
self.nameContainer.alpha = self.visibility;
|
||||
self.nameContainer.visible = YES;
|
||||
self.nameContainer.backgroundColor = [UIColor clearColor];
|
||||
self.avatarImageView.alpha = 0;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
self.avatarImageView.visible = NO;
|
||||
break;
|
||||
}
|
||||
case MPAvatarModeRaisedAndActive: {
|
||||
@@ -251,10 +252,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
self.nameContainer.alpha = self.visibility;
|
||||
self.nameContainer.visible = YES;
|
||||
self.nameContainer.backgroundColor = [UIColor blackColor];
|
||||
self.avatarImageView.alpha = 1;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
self.avatarImageView.visible = YES;
|
||||
break;
|
||||
}
|
||||
case MPAvatarModeRaisedAndHidden: {
|
||||
@@ -263,10 +263,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
self.nameContainer.alpha = 0;
|
||||
self.nameContainer.visible = NO;
|
||||
self.nameContainer.backgroundColor = [UIColor blackColor];
|
||||
self.avatarImageView.alpha = 0;
|
||||
self.avatarImageView.layer.shadowRadius = 15 * self.visibility * self.visibility;
|
||||
self.avatarImageView.visible = NO;
|
||||
break;
|
||||
}
|
||||
case MPAvatarModeRaisedAndMinimized: {
|
||||
@@ -274,9 +273,9 @@ const long MPAvatarAdd = 10000;
|
||||
[self.avatarRaisedConstraint updatePriority:UILayoutPriorityDefaultLow];
|
||||
[self.avatarToTopConstraint updatePriority:UILayoutPriorityDefaultHigh + 2];
|
||||
[self.nameToCenterConstraint updatePriority:UILayoutPriorityDefaultHigh];
|
||||
self.nameContainer.alpha = 0;
|
||||
self.nameContainer.visible = NO;
|
||||
self.nameContainer.backgroundColor = [UIColor blackColor];
|
||||
self.avatarImageView.alpha = 1;
|
||||
self.avatarImageView.visible = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -293,7 +292,7 @@ const long MPAvatarAdd = 10000;
|
||||
else
|
||||
self.avatarImageView.backgroundColor = [UIColor clearColor];
|
||||
self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.size.height / 2;
|
||||
self.spinner.alpha = self.spinnerActive? 1: 0;
|
||||
self.spinner.visible = self.spinnerActive;
|
||||
|
||||
[self.contentView layoutIfNeeded];
|
||||
} completion:nil];
|
||||
|
||||
@@ -28,17 +28,16 @@
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
_views = [NSArray arrayWithObjects:
|
||||
self.view0, self.view1, self.view2, self.view3, self.view4, self.view5, self.view6, self.view7, self.view8, self.view9, nil];
|
||||
_views = @[ self.view0, self.view1, self.view2, self.view3, self.view4, self.view5, self.view6, self.view7, self.view8, self.view9 ];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
self.viewProgress.hidden = NO;
|
||||
self.viewProgress.visible = YES;
|
||||
self.viewProgress.progress = 0;
|
||||
[_views makeObjectsPerformSelector:@selector( setAlpha: ) withObject:@0];
|
||||
[_views makeObjectsPerformSelector:@selector( setVisible: ) withObject:@NO];
|
||||
_nextView = 0;
|
||||
}
|
||||
|
||||
@@ -47,7 +46,7 @@
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
[_views[_nextView++] setAlpha:1];
|
||||
[_views[_nextView++] setVisible:YES];
|
||||
}];
|
||||
|
||||
_viewTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 block:^(NSTimer *timer) {
|
||||
@@ -56,11 +55,11 @@
|
||||
if (self.viewProgress.progress == 1)
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.viewProgress.progress = 0;
|
||||
[_views[_nextView++] setAlpha:1];
|
||||
[_views[_nextView++] setVisible:YES];
|
||||
|
||||
if (_nextView >= [_views count]) {
|
||||
[_viewTimer invalidate];
|
||||
self.viewProgress.hidden = YES;
|
||||
self.viewProgress.visible = NO;
|
||||
}
|
||||
}];
|
||||
} repeats:YES];
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *typeControl;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *counterLabel;
|
||||
@property(weak, nonatomic) IBOutlet UIActivityIndicatorView *activity;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *passwordLabel;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *passwordButton;
|
||||
@property(weak, nonatomic) IBOutlet UIView *tipContainer;
|
||||
|
||||
- (IBAction)controlChanged:(UIControl *)control;
|
||||
|
||||
@@ -81,22 +81,31 @@
|
||||
[self updatePassword];
|
||||
}
|
||||
|
||||
- (IBAction)copyPassword:(UITapGestureRecognizer *)recognizer {
|
||||
- (IBAction)copyPassword:(id)sender {
|
||||
|
||||
if (recognizer.state == UIGestureRecognizerStateEnded) {
|
||||
NSString *sitePassword = self.passwordLabel.text;
|
||||
if ([sitePassword length]) {
|
||||
[UIPasteboard generalPasteboard].string = sitePassword;
|
||||
NSString *sitePassword = [self.passwordButton titleForState:UIControlStateNormal];
|
||||
if (![sitePassword length])
|
||||
return;
|
||||
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
if ([pasteboard respondsToSelector:@selector( setItems:options: )])
|
||||
[pasteboard setItems:@[ @{ UIPasteboardTypeAutomatic: sitePassword } ]
|
||||
options:@{
|
||||
UIPasteboardOptionLocalOnly : @NO,
|
||||
UIPasteboardOptionExpirationDate: [NSDate dateWithTimeIntervalSinceNow:3 * 60]
|
||||
}];
|
||||
else
|
||||
pasteboard.string = sitePassword;
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.tipContainer.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
PearlMainQueueAfter( 3, ^{
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.tipContainer.alpha = 1;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished)
|
||||
PearlMainQueueAfter( 3, ^{
|
||||
self.tipContainer.alpha = 0;
|
||||
} );
|
||||
self.tipContainer.visible = NO;
|
||||
}];
|
||||
}
|
||||
}
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
@@ -106,7 +115,7 @@
|
||||
NSString *fullName = self.fullNameField.text;
|
||||
NSString *masterPassword = self.masterPasswordField.text;
|
||||
|
||||
self.passwordLabel.text = nil;
|
||||
[self.passwordButton setTitle:nil forState:UIControlStateNormal];
|
||||
[self.activity startAnimating];
|
||||
[_emergencyKeyQueue cancelAllOperations];
|
||||
[_emergencyKeyQueue addOperationWithBlock:^{
|
||||
@@ -128,7 +137,7 @@
|
||||
NSUInteger siteCounter = (NSUInteger)self.counterStepper.value;
|
||||
self.counterLabel.text = strf( @"%lu", (unsigned long)siteCounter );
|
||||
|
||||
self.passwordLabel.text = nil;
|
||||
[self.passwordButton setTitle:nil forState:UIControlStateNormal];
|
||||
[self.activity startAnimating];
|
||||
[_emergencyPasswordQueue cancelAllOperations];
|
||||
[_emergencyPasswordQueue addOperationWithBlock:^{
|
||||
@@ -138,7 +147,7 @@
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[self.activity stopAnimating];
|
||||
self.passwordLabel.text = sitePassword;
|
||||
[self.passwordButton setTitle:sitePassword forState:UIControlStateNormal];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[dismissButton addTarget:self action:@selector( dismissOverlay: ) forControlEvents:UIControlEventTouchUpInside];
|
||||
dismissButton.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5f];
|
||||
dismissButton.alpha = 0;
|
||||
dismissButton.visible = NO;
|
||||
dismissButton.frame = self.view.bounds;
|
||||
dismissButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_dismissSegueByButton[[NSValue valueWithNonretainedObject:dismissButton]] =
|
||||
@@ -89,7 +89,7 @@
|
||||
[_dismissSegueByButton removeObjectForKey:dismissSegueKey];
|
||||
|
||||
[UIView animateWithDuration:0.1f animations:^{
|
||||
dismissButton.alpha = 0;
|
||||
dismissButton.visible = NO;
|
||||
} completion:^(BOOL finished) {
|
||||
[dismissButton removeFromSuperview];
|
||||
}];
|
||||
@@ -127,14 +127,14 @@
|
||||
|
||||
CGRectSetY( destinationViewController.view.frame, 100 );
|
||||
destinationViewController.view.transform = CGAffineTransformMakeScale( 1.2f, 1.2f );
|
||||
destinationViewController.view.alpha = 0;
|
||||
destinationViewController.view.visible = NO;
|
||||
|
||||
[UIView transitionWithView:containerViewController.view duration:0.3f
|
||||
options:UIViewAnimationOptionAllowAnimatedContent animations:^{
|
||||
destinationViewController.view.transform = CGAffineTransformIdentity;
|
||||
CGRectSetY( destinationViewController.view.frame, 0 );
|
||||
destinationViewController.view.alpha = 1;
|
||||
dismissButton.alpha = 1;
|
||||
destinationViewController.view.visible = YES;
|
||||
dismissButton.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
[destinationViewController didMoveToParentViewController:containerViewController];
|
||||
[containerViewController setNeedsStatusBarAppearanceUpdate];
|
||||
@@ -147,7 +147,7 @@
|
||||
options:UIViewAnimationOptionAllowAnimatedContent animations:^{
|
||||
CGRectSetY( sourceViewController.view.frame, 100 );
|
||||
sourceViewController.view.transform = CGAffineTransformMakeScale( 0.8f, 0.8f );
|
||||
sourceViewController.view.alpha = 0;
|
||||
sourceViewController.view.visible = NO;
|
||||
[containerViewController removeDismissButtonForViewController:sourceViewController];
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
@property(nonatomic, strong) IBOutlet UITextField *passwordField;
|
||||
@property(nonatomic, strong) IBOutlet UIView *loginNameContainer;
|
||||
@property(nonatomic, strong) IBOutlet UITextField *loginNameField;
|
||||
@property(nonatomic, strong) IBOutlet UILabel *loginNameGenerated;
|
||||
@property(nonatomic, strong) IBOutlet UILabel *strengthLabel;
|
||||
@property(nonatomic, strong) IBOutlet UILabel *counterLabel;
|
||||
@property(nonatomic, strong) IBOutlet UIButton *counterButton;
|
||||
@@ -170,7 +171,7 @@
|
||||
|
||||
- (void)setSite:(MPSiteEntity *)site animated:(BOOL)animated {
|
||||
|
||||
_siteOID = [site objectID];
|
||||
_siteOID = site.permanentObjectID;
|
||||
[self updateAnimated:animated];
|
||||
}
|
||||
|
||||
@@ -199,7 +200,7 @@
|
||||
atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
|
||||
|
||||
if (textField == self.loginNameField)
|
||||
self.loginNameButton.titleLabel.alpha = [self.loginNameField.text length] || self.loginNameField.enabled? 0: 1;
|
||||
self.loginNameButton.hidden = [self.loginNameField.attributedText length] || self.loginNameField.enabled;
|
||||
}
|
||||
|
||||
- (IBAction)textFieldDidChange:(UITextField *)textField {
|
||||
@@ -224,7 +225,7 @@
|
||||
|
||||
if (textField == self.passwordField || textField == self.loginNameField) {
|
||||
textField.enabled = NO;
|
||||
NSString *text = textField.text;
|
||||
NSString *text = [textField.attributedText string]?: textField.text;
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *site = [self siteInContext:context];
|
||||
@@ -235,10 +236,8 @@
|
||||
if ([site.algorithm savePassword:text toSite:site usingKey:[MPiOSAppDelegate get].key])
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Updated" dismissAfter:2];
|
||||
}
|
||||
else if (textField == self.loginNameField &&
|
||||
((site.loginGenerated && ![text length]) ||
|
||||
(!site.loginGenerated && ![text isEqualToString:site.loginName]))) {
|
||||
if (site.loginGenerated || !([site.loginName isEqualToString:text] || (!text && !site.loginName))) {
|
||||
else if (textField == self.loginNameField) {
|
||||
if (![text isEqualToString:[site.algorithm resolveLoginForSite:site usingKey:[MPiOSAppDelegate get].key]]) {
|
||||
site.loginGenerated = NO;
|
||||
site.loginName = text;
|
||||
|
||||
@@ -282,12 +281,12 @@
|
||||
|
||||
[self setMode:MPPasswordCellModePassword animated:YES];
|
||||
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
[PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic
|
||||
initSheet:^(UIActionSheet *sheet) {
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) {
|
||||
for (NSNumber *typeNumber in [mainSite.algorithm allTypes]) {
|
||||
MPSiteType type = (MPSiteType)[typeNumber unsignedIntegerValue];
|
||||
NSString *typeName = [MPAlgorithmDefault nameOfType:type];
|
||||
NSString *typeName = [mainSite.algorithm nameOfType:type];
|
||||
if (type == mainSite.type)
|
||||
[sheet addButtonWithTitle:strf( @"● %@", typeName )];
|
||||
else
|
||||
@@ -297,7 +296,8 @@
|
||||
if (buttonIndex == [sheet cancelButtonIndex])
|
||||
return;
|
||||
|
||||
MPSiteType type = (MPSiteType)[[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPSiteTypeGeneratedLong;
|
||||
MPSiteType type = (MPSiteType)[[mainSite.algorithm allTypes][buttonIndex] unsignedIntegerValue]?:
|
||||
mainSite.user.defaultType?: mainSite.algorithm.defaultType;
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *site = [self siteInContext:context];
|
||||
@@ -367,6 +367,31 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)doAction:(UIButton *)sender {
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
|
||||
MPSiteEntity *mainSite = [self siteInContext:mainContext];
|
||||
[PearlAlert showAlertWithTitle:@"Login Page" message:nil
|
||||
viewStyle:UIAlertViewStylePlainTextInput
|
||||
initAlert:^(UIAlertView *alert, UITextField *firstField) {
|
||||
firstField.placeholder = strf( @"Login URL for %@", mainSite.name );
|
||||
firstField.text = mainSite.url;
|
||||
}
|
||||
tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||
if (buttonIndex == alert.cancelButtonIndex)
|
||||
return;
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *site = [self siteInContext:context];
|
||||
NSURL *url = [NSURL URLWithString:[alert textFieldAtIndex:0].text];
|
||||
site.url = [url.host? url: nil absoluteString];
|
||||
[context saveToStore];
|
||||
}];
|
||||
}
|
||||
cancelTitle:@"Cancel" otherTitles:@"Save", nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)doIncrementCounter:(UIButton *)sender {
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
@@ -493,10 +518,12 @@
|
||||
|
||||
- (void)updateAnimated:(BOOL)animated {
|
||||
|
||||
Weakify( self );
|
||||
if (![NSThread isMainThread]) {
|
||||
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
|
||||
PearlMainQueueOperation( ^{
|
||||
Strongify( self );
|
||||
[self updateAnimated:animated];
|
||||
}];
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -504,13 +531,14 @@
|
||||
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
|
||||
|
||||
// UI
|
||||
self.backgroundColor = mainSite.url? [UIColor greenColor]: [UIColor redColor];
|
||||
self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue];
|
||||
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
|
||||
BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
|
||||
self.loginNameContainer.alpha = settingsMode || mainSite.loginGenerated || [mainSite.loginName length]? 0.7f: 0;
|
||||
self.loginNameField.textColor = [UIColor colorWithHexString:mainSite.loginGenerated? @"5E636D": @"6D5E63"];
|
||||
self.modeButton.alpha = self.transientSite? 0: settingsMode? 0.5f: 0.1f;
|
||||
self.counterLabel.alpha = self.counterButton.alpha = mainSite.type & MPSiteTypeClassGenerated? 0.5f: 0;
|
||||
self.loginNameContainer.visible = settingsMode || mainSite.loginGenerated || [mainSite.loginName length];
|
||||
self.modeButton.visible = !self.transientSite;
|
||||
self.modeButton.alpha = settingsMode? 0.5f: 0.1f;
|
||||
self.counterLabel.visible = self.counterButton.visible = mainSite.type & MPSiteTypeClassGenerated;
|
||||
self.modeButton.selected = settingsMode;
|
||||
self.strengthLabel.gone = !settingsMode;
|
||||
self.modeScrollView.scrollEnabled = !self.transientSite;
|
||||
@@ -520,13 +548,21 @@
|
||||
[self.passwordField resignFirstResponder];
|
||||
}
|
||||
if ([[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateLogins])
|
||||
[self.loginNameButton setTitle:@"Tap to generate username or use pencil to save one" forState:UIControlStateNormal];
|
||||
[self.loginNameButton setTitle:@"Tap here to ⚙ generate username or the pencil to type one" forState:UIControlStateNormal];
|
||||
else
|
||||
[self.loginNameButton setTitle:@"Tap the pencil to save a username" forState:UIControlStateNormal];
|
||||
[self.loginNameButton setTitle:@"Tap the pencil to type a username" forState:UIControlStateNormal];
|
||||
|
||||
// Site Name
|
||||
[self updateSiteName:mainSite];
|
||||
|
||||
// Site Counter
|
||||
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
|
||||
|
||||
// Site Login Name
|
||||
self.loginNameField.enabled = self.passwordField.enabled = //
|
||||
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
|
||||
|
||||
// Site Password
|
||||
self.passwordField.secureTextEntry = [[MPiOSConfig get].hidePasswords boolValue];
|
||||
self.passwordField.attributedPlaceholder = stra(
|
||||
@@ -534,14 +570,23 @@
|
||||
mainSite.type & MPSiteTypeClassGenerated? strl( @"..." ): @"", @{
|
||||
NSForegroundColorAttributeName: [UIColor whiteColor]
|
||||
} );
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
|
||||
// Calculate Fields
|
||||
if (![MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPSiteEntity *site = [self siteInContext:context];
|
||||
MPKey *key = [MPiOSAppDelegate get].key;
|
||||
if (!key)
|
||||
if (!key) {
|
||||
wrn( @"Could not load cell content: key unavailable." );
|
||||
PearlMainQueueOperation( ^{
|
||||
Strongify( self );
|
||||
[self updateAnimated:YES];
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL loginGenerated = site.loginGenerated;
|
||||
NSString *password = nil, *loginName = [site resolveLoginUsingKey:key];
|
||||
MPSiteType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPSiteTypeGeneratedLong;
|
||||
MPSiteType transientType = [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPAlgorithmDefault.defaultType;
|
||||
if (self.transientSite && transientType & MPSiteTypeClassGenerated)
|
||||
password = [MPAlgorithmDefault generatePasswordForSiteNamed:self.transientSite ofType:transientType
|
||||
withCounter:1 usingKey:key];
|
||||
@@ -559,13 +604,15 @@
|
||||
BOOL requiresExplicitMigration = site.requiresExplicitMigration;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
self.loginNameField.text = loginName;
|
||||
self.passwordField.text = password;
|
||||
self.strengthLabel.text = timeToCrackString;
|
||||
self.loginNameButton.titleLabel.alpha = [loginName length] || self.loginNameField.enabled? 0: 1;
|
||||
self.loginNameGenerated.hidden = !loginGenerated;
|
||||
self.loginNameField.attributedText =
|
||||
strarm( stra( loginName?: @"", self.siteNameLabel.textAttributes ), NSParagraphStyleAttributeName, nil );
|
||||
self.loginNameButton.hidden = [loginName length] || self.loginNameField.enabled;
|
||||
|
||||
if (![password length]) {
|
||||
self.indicatorView.alpha = 1;
|
||||
self.indicatorView.hidden = NO;
|
||||
[self.indicatorView removeFromSuperview];
|
||||
[self.modeScrollView addSubview:self.indicatorView];
|
||||
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
|
||||
@@ -575,7 +622,7 @@
|
||||
}];
|
||||
}
|
||||
else if (requiresExplicitMigration) {
|
||||
self.indicatorView.alpha = 1;
|
||||
self.indicatorView.hidden = NO;
|
||||
[self.indicatorView removeFromSuperview];
|
||||
[self.modeScrollView addSubview:self.indicatorView];
|
||||
[self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
|
||||
@@ -585,17 +632,15 @@
|
||||
}];
|
||||
}
|
||||
else
|
||||
self.indicatorView.alpha = 0;
|
||||
self.indicatorView.hidden = YES;
|
||||
} );
|
||||
}];
|
||||
|
||||
// Site Counter
|
||||
if ([mainSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
self.counterLabel.text = strf( @"%lu", (unsigned long)((MPGeneratedSiteEntity *)mainSite).counter );
|
||||
|
||||
// Site Login Name
|
||||
self.loginNameField.enabled = self.passwordField.enabled = //
|
||||
[self.loginNameField isFirstResponder] || [self.passwordField isFirstResponder];
|
||||
}]) {
|
||||
wrn( @"Could not load cell content: store unavailable." );
|
||||
PearlMainQueueOperation( ^{
|
||||
Strongify( self );
|
||||
[self updateAnimated:YES];
|
||||
} );
|
||||
}
|
||||
|
||||
[self.contentView layoutIfNeeded];
|
||||
}];
|
||||
@@ -616,8 +661,9 @@
|
||||
range:NSMakeRange( s, [self.fuzzyGroups[f] length] )];
|
||||
}
|
||||
|
||||
[attributedSiteName appendAttributedString:stra(
|
||||
strf( @" - %@", self.transientSite? @"Tap to create": [site.algorithm shortNameOfType:site.type] ), @{} )];
|
||||
if (self.transientSite)
|
||||
[attributedSiteName appendAttributedString:stra( @" – Tap to create", @{} )];
|
||||
|
||||
self.siteNameLabel.attributedText = attributedSiteName;
|
||||
}
|
||||
|
||||
@@ -629,8 +675,21 @@
|
||||
return NO;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied" ) dismissAfter:2];
|
||||
[UIPasteboard generalPasteboard].string = password;
|
||||
[self.window endEditing:YES];
|
||||
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
if ([pasteboard respondsToSelector:@selector(setItems:options:)]) {
|
||||
[pasteboard setItems:@[ @{ UIPasteboardTypeAutomatic: password } ]
|
||||
options:@{
|
||||
UIPasteboardOptionLocalOnly : @NO,
|
||||
UIPasteboardOptionExpirationDate: [NSDate dateWithTimeIntervalSinceNow:3 * 60]
|
||||
}];
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied (3 min)" ) dismissAfter:2];
|
||||
}
|
||||
else {
|
||||
pasteboard.string = password;
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Password Copied" ) dismissAfter:2];
|
||||
}
|
||||
} );
|
||||
|
||||
[site use];
|
||||
@@ -646,8 +705,10 @@
|
||||
return NO;
|
||||
|
||||
PearlMainQueue( ^{
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Login Name Copied" ) dismissAfter:2];
|
||||
[self.window endEditing:YES];
|
||||
|
||||
[UIPasteboard generalPasteboard].string = loginName;
|
||||
[PearlOverlay showTemporaryOverlayWithTitle:strl( @"Login Name Copied" ) dismissAfter:2];
|
||||
} );
|
||||
|
||||
[site use];
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
@property(assign, nonatomic) BOOL active;
|
||||
|
||||
- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void ( ^ )(BOOL finished))completion;
|
||||
- (void)updatePasswords;
|
||||
- (void)reloadPasswords;
|
||||
|
||||
- (IBAction)dismissPopdown:(id)sender;
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#import "MPAnswersViewController.h"
|
||||
#import "MPMessageViewController.h"
|
||||
|
||||
static const NSString *MPTransientPasswordItem = @"MPTransientPasswordItem";
|
||||
|
||||
typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
MPPasswordsBadNameTip = 1 << 0,
|
||||
};
|
||||
@@ -32,7 +34,6 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
@interface MPPasswordsViewController()<NSFetchedResultsControllerDelegate>
|
||||
|
||||
@property(nonatomic, strong) IBOutlet UINavigationBar *navigationBar;
|
||||
@property(nonatomic, readonly) NSString *query;
|
||||
|
||||
@end
|
||||
|
||||
@@ -42,10 +43,9 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
UIColor *_backgroundColor;
|
||||
UIColor *_darkenedBackgroundColor;
|
||||
__weak UIViewController *_popdownVC;
|
||||
BOOL _showTransientItem;
|
||||
NSUInteger _transientItem;
|
||||
NSCharacterSet *_siteNameAcceptableCharactersSet;
|
||||
NSArray *_fuzzyGroups;
|
||||
NSMutableArray<NSMutableArray *> *_passwordCollectionSections;
|
||||
}
|
||||
|
||||
#pragma mark - Life
|
||||
@@ -61,7 +61,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
_backgroundColor = self.passwordCollectionView.backgroundColor;
|
||||
_darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f];
|
||||
_transientItem = NSNotFound;
|
||||
_passwordCollectionSections = [NSMutableArray new];
|
||||
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
[self.passwordCollectionView automaticallyAdjustInsetsForKeyboard];
|
||||
@@ -81,7 +81,24 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
[self registerObservers];
|
||||
[self updateConfigKey:nil];
|
||||
[self updatePasswords];
|
||||
|
||||
static NSRegularExpression *bareHostRE = nil;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
bareHostRE = [NSRegularExpression regularExpressionWithPattern:@"([^\\.]+\\.[^\\.]+)$" options:0 error:nil];
|
||||
} );
|
||||
|
||||
NSURL *pasteboardURL = nil;
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
if ([pasteboard respondsToSelector:@selector( hasURLs )])
|
||||
pasteboardURL = pasteboard.hasURLs? pasteboard.URL: nil;
|
||||
else
|
||||
pasteboardURL = [NSURL URLWithString:pasteboard.string];
|
||||
|
||||
if (pasteboardURL.host)
|
||||
self.query = NSNullToNil( [pasteboardURL.host firstMatchGroupsOfExpression:bareHostRE][0] );
|
||||
else
|
||||
[self reloadPasswords];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
@@ -138,30 +155,43 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
return CGSizeMake( itemWidth, 100 );
|
||||
}
|
||||
|
||||
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout
|
||||
insetForSectionAtIndex:(NSInteger)section {
|
||||
|
||||
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
|
||||
UIEdgeInsets occludedInsets = [self.passwordCollectionView occludedInsets];
|
||||
UIEdgeInsets insets = layout.sectionInset;
|
||||
insets.top = insets.bottom; // Undo storyboard hack for manual top-occluded insets.
|
||||
|
||||
if (section == 0)
|
||||
insets.top += occludedInsets.top;
|
||||
|
||||
if (section == collectionView.numberOfSections - 1)
|
||||
insets.bottom += occludedInsets.bottom;
|
||||
|
||||
return insets;
|
||||
}
|
||||
|
||||
#pragma mark - UICollectionViewDataSource
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
|
||||
|
||||
return [self.fetchedResultsController.sections count];
|
||||
return [_passwordCollectionSections count];
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
|
||||
if (![MPiOSAppDelegate get].activeUserOID || !_fetchedResultsController)
|
||||
return 0;
|
||||
|
||||
NSUInteger objects = ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[section]).numberOfObjects;
|
||||
_transientItem = _showTransientItem? objects: NSNotFound;
|
||||
return objects + (_showTransientItem? 1: 0);
|
||||
return [_passwordCollectionSections[(NSUInteger)section] count];
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
MPPasswordCell *cell = [MPPasswordCell dequeueCellFromCollectionView:collectionView indexPath:indexPath];
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
|
||||
[cell setSite:[self.fetchedResultsController objectAtIndexPath:indexPath] animated:NO];
|
||||
else
|
||||
id item = _passwordCollectionSections[(NSUInteger)indexPath.section][(NSUInteger)indexPath.item];
|
||||
if ([item isKindOfClass:[MPSiteEntity class]])
|
||||
[cell setSite:item animated:NO];
|
||||
else // item == MPTransientPasswordItem
|
||||
[cell setTransientSite:self.query animated:NO];
|
||||
|
||||
return cell;
|
||||
@@ -178,41 +208,14 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
#pragma mark - NSFetchedResultsControllerDelegate
|
||||
|
||||
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
|
||||
forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
|
||||
|
||||
if (controller == _fetchedResultsController) {
|
||||
@try {
|
||||
[self.passwordCollectionView performBatchUpdates:^{
|
||||
[self fetchedItemsDidUpdate];
|
||||
switch (type) {
|
||||
case NSFetchedResultsChangeInsert:
|
||||
[self.passwordCollectionView insertItemsAtIndexPaths:@[ newIndexPath ]];
|
||||
break;
|
||||
case NSFetchedResultsChangeDelete:
|
||||
[self.passwordCollectionView deleteItemsAtIndexPaths:@[ indexPath ]];
|
||||
break;
|
||||
case NSFetchedResultsChangeMove:
|
||||
[self.passwordCollectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
|
||||
break;
|
||||
case NSFetchedResultsChangeUpdate:
|
||||
[self.passwordCollectionView reloadItemsAtIndexPaths:@[ indexPath ]];
|
||||
break;
|
||||
}
|
||||
} completion:nil];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
wrn( @"While updating password cells: %@", [exception fullDescription] );
|
||||
[self.passwordCollectionView reloadData];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
|
||||
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
|
||||
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
|
||||
|
||||
if (controller == _fetchedResultsController)
|
||||
[self.passwordCollectionView reloadData];
|
||||
PearlMainQueue( ^{
|
||||
[self.passwordCollectionView updateDataSource:_passwordCollectionSections
|
||||
toSections:[self createPasswordCollectionSections]
|
||||
reloadItems:nil completion:nil];
|
||||
} );
|
||||
}
|
||||
|
||||
#pragma mark - UISearchBarDelegate
|
||||
@@ -244,7 +247,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
if (_passwordsDismissRecognizer)
|
||||
[self.view removeGestureRecognizer:_passwordsDismissRecognizer];
|
||||
|
||||
[self updatePasswords];
|
||||
[self reloadPasswords];
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.passwordCollectionView.backgroundColor = _backgroundColor;
|
||||
}];
|
||||
@@ -268,7 +271,7 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
if ([[self.query stringByTrimmingCharactersInSet:_siteNameAcceptableCharactersSet] length])
|
||||
[self showTips:MPPasswordsBadNameTip];
|
||||
|
||||
[self updatePasswords];
|
||||
[self reloadPasswords];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,62 +281,65 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPPasswordsBadNameTip)
|
||||
self.badNameTipContainer.alpha = 1;
|
||||
self.badNameTipContainer.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished)
|
||||
PearlMainQueueAfter( 5, ^{
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPPasswordsBadNameTip)
|
||||
self.badNameTipContainer.alpha = 0;
|
||||
}];
|
||||
} );
|
||||
PearlMainQueueAfter( 5, ^{
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPPasswordsBadNameTip)
|
||||
self.badNameTipContainer.visible = NO;
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fetchedItemsDidUpdate {
|
||||
- (NSMutableArray<NSMutableArray *> *)createPasswordCollectionSections {
|
||||
|
||||
NSString *query = self.query;
|
||||
_showTransientItem = [query length] > 0;
|
||||
NSUInteger objects = ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[0]).numberOfObjects;
|
||||
if (_showTransientItem && objects == 1 &&
|
||||
[[[self.fetchedResultsController.fetchedObjects firstObject] name] isEqualToString:query])
|
||||
_showTransientItem = NO;
|
||||
if ([self.passwordCollectionView numberOfSections] > 0) {
|
||||
if (!_showTransientItem && _transientItem != NSNotFound)
|
||||
[self.passwordCollectionView deleteItemsAtIndexPaths:
|
||||
@[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]];
|
||||
else if (_showTransientItem && _transientItem == NSNotFound)
|
||||
[self.passwordCollectionView insertItemsAtIndexPaths:
|
||||
@[ [NSIndexPath indexPathForItem:objects inSection:0] ]];
|
||||
else if (_transientItem != NSNotFound)
|
||||
[self.passwordCollectionView reloadItemsAtIndexPaths:
|
||||
@[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]];
|
||||
BOOL needTransientItem = [query length] > 0;
|
||||
|
||||
NSArray<id<NSFetchedResultsSectionInfo>> *sectionInfos = [self.fetchedResultsController sections];
|
||||
NSMutableArray *sections = [[NSMutableArray alloc] initWithCapacity:[sectionInfos count]];
|
||||
for (id<NSFetchedResultsSectionInfo> sectionInfo in sectionInfos) {
|
||||
NSArray<MPSiteEntity *> *sites = [sectionInfo.objects copy];
|
||||
[sections addObject:sites];
|
||||
|
||||
if (needTransientItem)
|
||||
for (MPSiteEntity *site in sites)
|
||||
if ([site.name isEqualToString:query]) {
|
||||
needTransientItem = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needTransientItem)
|
||||
[sections addObject:@[ MPTransientPasswordItem ]];
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
- (void)registerObservers {
|
||||
|
||||
PearlRemoveNotificationObservers();
|
||||
PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
PearlAddNotificationObserver( UIApplicationWillResignActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
self.passwordSelectionContainer.alpha = 0;
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
[self updatePasswords];
|
||||
[self.view endEditing:YES];
|
||||
self.passwordSelectionContainer.visible = NO;
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
[UIView animateWithDuration:0.7f animations:^{
|
||||
self.passwordSelectionContainer.alpha = 1;
|
||||
self.passwordSelectionContainer.visible = YES;
|
||||
}];
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
[self viewWillAppear:YES];
|
||||
} );
|
||||
PearlAddNotificationObserver( MPSignedOutNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
_fetchedResultsController = nil;
|
||||
self.passwordsSearchBar.text = nil;
|
||||
[self.passwordCollectionView reloadData];
|
||||
self->_fetchedResultsController = nil;
|
||||
self.query = nil;
|
||||
} );
|
||||
} );
|
||||
PearlAddNotificationObserver( MPCheckConfigNotification, nil, nil,
|
||||
@@ -345,25 +351,23 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
self->_fetchedResultsController = nil;
|
||||
PearlMainQueue( ^{
|
||||
[self.passwordCollectionView reloadData];
|
||||
} );
|
||||
[self reloadPasswords];
|
||||
} );
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, nil, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
[self updatePasswords];
|
||||
[self reloadPasswords];
|
||||
[self registerObservers];
|
||||
} );
|
||||
} );
|
||||
|
||||
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
if (mainContext)
|
||||
PearlAddNotificationObserver( NSManagedObjectContextDidSaveNotification, mainContext, nil,
|
||||
^(MPPasswordsViewController *self, NSNotification *note) {
|
||||
if (![[MPiOSAppDelegate get] activeUserInContext:note.object])
|
||||
[[MPiOSAppDelegate get] signOutAnimated:YES];
|
||||
} );
|
||||
[MPiOSAppDelegate managedObjectContextChanged:^(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects) {
|
||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlock:^(NSManagedObjectContext *mainContext) {
|
||||
// TODO: either move this into the app delegate or remove the duplicate signOutAnimated: call from the app delegate.
|
||||
if (![[MPiOSAppDelegate get] activeUserInContext:mainContext])
|
||||
[[MPiOSAppDelegate get] signOutAnimated:YES];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateConfigKey:(NSString *)key {
|
||||
@@ -374,81 +378,38 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
[self.passwordCollectionView reloadData];
|
||||
}
|
||||
|
||||
- (void)updatePasswords {
|
||||
- (void)reloadPasswords {
|
||||
|
||||
NSManagedObjectID *activeUserOID = [MPiOSAppDelegate get].activeUserOID;
|
||||
if (!activeUserOID) {
|
||||
PearlMainQueue( ^{
|
||||
self.passwordsSearchBar.text = nil;
|
||||
[self.passwordCollectionView reloadData];
|
||||
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top ) animated:YES];
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
static NSRegularExpression *fuzzyRE;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
|
||||
} );
|
||||
|
||||
NSString *queryString = self.query;
|
||||
NSString *queryPattern;
|
||||
if ([queryString length] < 13)
|
||||
queryPattern = [queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1*"];
|
||||
else
|
||||
// If query is too long, a wildcard per character makes the CoreData fetch take excessively long.
|
||||
queryPattern = strf( @"*%@*", queryString );
|
||||
NSMutableArray *fuzzyGroups = [NSMutableArray arrayWithCapacity:[queryString length]];
|
||||
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
|
||||
}];
|
||||
_fuzzyGroups = fuzzyGroups;
|
||||
[self.fetchedResultsController.managedObjectContext performBlock:^{
|
||||
NSArray *oldSectionInfos = [self.fetchedResultsController sections];
|
||||
NSMutableArray *oldSections = [[NSMutableArray alloc] initWithCapacity:[oldSectionInfos count]];
|
||||
for (id<NSFetchedResultsSectionInfo> sectionInfo in oldSectionInfos)
|
||||
[oldSections addObject:[sectionInfo.objects copy]];
|
||||
static NSRegularExpression *fuzzyRE;
|
||||
static dispatch_once_t once = 0;
|
||||
dispatch_once( &once, ^{
|
||||
fuzzyRE = [NSRegularExpression regularExpressionWithPattern:@"(.)" options:0 error:nil];
|
||||
} );
|
||||
|
||||
NSString *queryString = self.query;
|
||||
NSString *queryPattern = [[queryString stringByReplacingMatchesOfExpression:fuzzyRE withTemplate:@"*$1"]
|
||||
stringByAppendingString:@"*"];
|
||||
NSMutableArray *fuzzyGroups = [NSMutableArray new];
|
||||
[fuzzyRE enumerateMatchesInString:queryString options:0 range:NSMakeRange( 0, queryString.length )
|
||||
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
|
||||
[fuzzyGroups addObject:[queryString substringWithRange:result.range]];
|
||||
}];
|
||||
_fuzzyGroups = fuzzyGroups;
|
||||
|
||||
NSError *error = nil;
|
||||
self.fetchedResultsController.fetchRequest.predicate =
|
||||
[NSPredicate predicateWithFormat:@"(%@ == '' OR name LIKE[cd] %@) AND user == %@",
|
||||
queryPattern, queryPattern, activeUserOID];
|
||||
[NSPredicate predicateWithFormat:@"name LIKE[cd] %@ AND user == %@", queryPattern, [MPiOSAppDelegate get].activeUserOID];
|
||||
if (![self.fetchedResultsController performFetch:&error])
|
||||
err( @"Couldn't fetch sites: %@", [error fullDescription] );
|
||||
MPError( error, @"Couldn't fetch sites." );
|
||||
|
||||
PearlMainQueue( ^{
|
||||
@try {
|
||||
[self.passwordCollectionView performBatchUpdates:^{
|
||||
[self fetchedItemsDidUpdate];
|
||||
|
||||
NSInteger fromSections = self.passwordCollectionView.numberOfSections;
|
||||
NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView];
|
||||
for (NSInteger section = 0; section < MAX( toSections, fromSections ); ++section) {
|
||||
if (section >= fromSections)
|
||||
[self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]];
|
||||
else if (section >= toSections)
|
||||
[self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
|
||||
else if (section < [oldSections count])
|
||||
[self.passwordCollectionView reloadItemsFromArray:oldSections[section]
|
||||
toArray:[[self.fetchedResultsController sections][section] objects]
|
||||
inSection:section];
|
||||
else
|
||||
[self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]];
|
||||
}
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished)
|
||||
[self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top )
|
||||
animated:YES];
|
||||
for (MPPasswordCell *cell in self.passwordCollectionView.visibleCells)
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
}];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
wrn( @"While updating password cells: %@", [exception fullDescription] );
|
||||
[self.passwordCollectionView reloadData];
|
||||
}
|
||||
[self.passwordCollectionView updateDataSource:_passwordCollectionSections
|
||||
toSections:[self createPasswordCollectionSections]
|
||||
reloadItems:@[ MPTransientPasswordItem ] completion:^(BOOL finished) {
|
||||
for (MPPasswordCell *cell in self.passwordCollectionView.visibleCells)
|
||||
[cell setFuzzyGroups:_fuzzyGroups];
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
@@ -460,18 +421,24 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
|
||||
return [self.passwordsSearchBar.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
}
|
||||
|
||||
- (void)setQuery:(NSString *)query {
|
||||
|
||||
self.passwordsSearchBar.text = [query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
[self reloadPasswords];
|
||||
}
|
||||
|
||||
- (NSFetchedResultsController *)fetchedResultsController {
|
||||
|
||||
if (!_fetchedResultsController) {
|
||||
_showTransientItem = NO;
|
||||
[MPiOSAppDelegate managedObjectContextForMainThreadPerformBlockAndWait:^(NSManagedObjectContext *mainContext) {
|
||||
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
fetchRequest.sortDescriptors = @[
|
||||
[[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
||||
];
|
||||
fetchRequest.fetchBatchSize = 10;
|
||||
_fetchedResultsController = [[NSFetchedResultsController alloc]
|
||||
initWithFetchRequest:fetchRequest managedObjectContext:mainContext sectionNameKeyPath:nil cacheName:nil];
|
||||
_fetchedResultsController =
|
||||
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:mainContext
|
||||
sectionNameKeyPath:nil cacheName:nil];
|
||||
_fetchedResultsController.delegate = self;
|
||||
}];
|
||||
[self registerObservers];
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
@implementation MPPopdownSegue {
|
||||
}
|
||||
|
||||
static char UnwindingObserverKey;
|
||||
|
||||
- (void)perform {
|
||||
|
||||
MPPasswordsViewController *passwordsVC;
|
||||
@@ -39,19 +37,21 @@ static char UnwindingObserverKey;
|
||||
[passwordsVC.popdownContainer addConstraintsWithVisualFormats:@[ @"H:|[popdownView]|", @"V:|[popdownView]|" ] options:0
|
||||
metrics:nil views:NSDictionaryOfVariableBindings( popdownView )];
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
[[passwordsVC.popdownToTopConstraint updatePriority:1] layoutIfNeeded];
|
||||
} completion:^(BOOL finished) {
|
||||
[popdownVC didMoveToParentViewController:passwordsVC];
|
||||
[passwordsVC.popdownToTopConstraint layoutIfNeeded];
|
||||
[passwordsVC.view endEditing:YES];
|
||||
|
||||
id<NSObject> observer = [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil
|
||||
queue:[NSOperationQueue mainQueue] usingBlock:
|
||||
^(NSNotification *note) {
|
||||
[[[MPPopdownSegue alloc] initWithIdentifier:@"unwind-popdown" source:popdownVC
|
||||
destination:passwordsVC] perform];
|
||||
}];
|
||||
objc_setAssociatedObject( popdownVC, &UnwindingObserverKey, observer, OBJC_ASSOCIATION_RETAIN );
|
||||
}];
|
||||
[UIView animateWithDuration:0.6f delay:0 usingSpringWithDamping:0.75f initialSpringVelocity:1
|
||||
options:UIViewAnimationOptionCurveEaseOut animations:^{
|
||||
[[passwordsVC.popdownToTopConstraint updatePriority:1] layoutIfNeeded];
|
||||
} completion:^(BOOL finished) {
|
||||
[popdownVC didMoveToParentViewController:passwordsVC];
|
||||
|
||||
PearlAddNotificationObserverTo( popdownVC, MPSignedOutNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(id host, NSNotification *note) {
|
||||
[[[MPPopdownSegue alloc] initWithIdentifier:@"unwind-popdown" source:popdownVC destination:passwordsVC]
|
||||
perform];
|
||||
} );
|
||||
}];
|
||||
}
|
||||
else {
|
||||
popdownVC = self.sourceViewController;
|
||||
@@ -59,16 +59,16 @@ static char UnwindingObserverKey;
|
||||
passwordsVC = (id)passwordsVC.parentViewController);
|
||||
NSAssert( passwordsVC, @"Couldn't find passwords VC to pop back to." );
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:objc_getAssociatedObject( popdownVC, &UnwindingObserverKey )];
|
||||
objc_setAssociatedObject( popdownVC, &UnwindingObserverKey, nil, OBJC_ASSOCIATION_RETAIN );
|
||||
PearlRemoveNotificationObserversFrom( popdownVC );
|
||||
|
||||
[popdownVC willMoveToParentViewController:nil];
|
||||
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionOverrideInheritedDuration animations:^{
|
||||
[[passwordsVC.popdownToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded];
|
||||
} completion:^(BOOL finished) {
|
||||
[popdownVC.view removeFromSuperview];
|
||||
[popdownVC removeFromParentViewController];
|
||||
}];
|
||||
[UIView animateWithDuration:0.4f delay:0 options:UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionOverrideInheritedDuration
|
||||
animations:^{
|
||||
[[passwordsVC.popdownToTopConstraint updatePriority:UILayoutPriorityDefaultHigh] layoutIfNeeded];
|
||||
} completion:^(BOOL finished) {
|
||||
[popdownVC.view removeFromSuperview];
|
||||
[popdownVC removeFromParentViewController];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,10 @@
|
||||
@property(weak, nonatomic) IBOutlet UITableViewCell *exportCell;
|
||||
@property(weak, nonatomic) IBOutlet UITableViewCell *checkInconsistencies;
|
||||
@property(weak, nonatomic) IBOutlet UIImageView *avatarImage;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *generatedTypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *generated1TypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *generated2TypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UISegmentedControl *storedTypeControl;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *passwordTypeExample;
|
||||
|
||||
- (IBAction)previousAvatar:(id)sender;
|
||||
- (IBAction)nextAvatar:(id)sender;
|
||||
|
||||
@@ -55,12 +55,24 @@
|
||||
- (void)reload {
|
||||
|
||||
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserForMainThread];
|
||||
self.generatedTypeControl.selectedSegmentIndex = [self generatedSegmentIndexForType:activeUser.defaultType];
|
||||
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:activeUser.defaultType];
|
||||
self.avatarImage.image = [UIImage imageNamed:strf( @"avatar-%lu", (unsigned long)activeUser.avatar )];
|
||||
self.savePasswordSwitch.on = activeUser.saveKey;
|
||||
self.touchIDSwitch.on = activeUser.touchID;
|
||||
self.touchIDSwitch.enabled = self.savePasswordSwitch.on && [[MPiOSAppDelegate get] isFeatureUnlocked:MPProductTouchID];
|
||||
|
||||
MPSiteType defaultType = activeUser.defaultType;
|
||||
self.generated1TypeControl.selectedSegmentIndex = [self generated1SegmentIndexForType:defaultType];
|
||||
self.generated2TypeControl.selectedSegmentIndex = [self generated2SegmentIndexForType:defaultType];
|
||||
self.storedTypeControl.selectedSegmentIndex = [self storedSegmentIndexForType:defaultType];
|
||||
PearlNotMainQueue( ^{
|
||||
NSString *examplePassword = nil;
|
||||
if (defaultType & MPSiteTypeClassGenerated)
|
||||
examplePassword = [MPAlgorithmDefault generatePasswordForSiteNamed:@"test" ofType:defaultType
|
||||
withCounter:1 usingKey:[MPiOSAppDelegate get].key];
|
||||
PearlMainQueue( ^{
|
||||
self.passwordTypeExample.text = [examplePassword length]? [NSString stringWithFormat:@"eg. %@", examplePassword]: nil;
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
@@ -88,14 +100,18 @@
|
||||
[self dismissPopup];
|
||||
[[MPiOSAppDelegate get] signOutAnimated:YES];
|
||||
}
|
||||
|
||||
if (cell == self.feedbackCell)
|
||||
[[MPiOSAppDelegate get] showFeedbackWithLogs:YES forVC:self];
|
||||
|
||||
if (cell == self.exportCell)
|
||||
[[MPiOSAppDelegate get] showExportForVC:self];
|
||||
|
||||
if (cell == self.showHelpCell) {
|
||||
MPPasswordsViewController *passwordsVC = [self dismissPopup];
|
||||
[passwordsVC performSegueWithIdentifier:@"guide" sender:self];
|
||||
}
|
||||
|
||||
if (cell == self.checkInconsistencies)
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
if ([[MPiOSAppDelegate get] findAndFixInconsistenciesSaveInContext:context] == MPFixableResultNoProblems)
|
||||
@@ -140,11 +156,13 @@
|
||||
} );
|
||||
}];
|
||||
|
||||
if (sender == self.generatedTypeControl || sender == self.storedTypeControl) {
|
||||
if (sender == self.generatedTypeControl)
|
||||
if (sender == self.generated1TypeControl || sender == self.generated2TypeControl || sender == self.storedTypeControl) {
|
||||
if (sender != self.generated1TypeControl)
|
||||
self.generated1TypeControl.selectedSegmentIndex = -1;
|
||||
if (sender != self.generated2TypeControl)
|
||||
self.generated2TypeControl.selectedSegmentIndex = -1;
|
||||
if (sender != self.storedTypeControl)
|
||||
self.storedTypeControl.selectedSegmentIndex = -1;
|
||||
else if (sender == self.storedTypeControl)
|
||||
self.generatedTypeControl.selectedSegmentIndex = -1;
|
||||
|
||||
MPSiteType defaultType = [self typeForSelectedSegment];
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
@@ -189,13 +207,13 @@
|
||||
- (IBAction)homePageButton:(id)sender {
|
||||
|
||||
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
|
||||
[NSURL URLWithString:@"http://masterpasswordapp.com"]];
|
||||
[NSURL URLWithString:@"https://ssl.masterpasswordapp.com"]];
|
||||
}
|
||||
|
||||
- (IBAction)securityButton:(id)sender {
|
||||
|
||||
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
|
||||
[NSURL URLWithString:@"http://masterpasswordapp.com/security.html"]];
|
||||
[NSURL URLWithString:@"https://ssl.masterpasswordapp.com/security.html"]];
|
||||
}
|
||||
|
||||
- (IBAction)sourceButton:(id)sender {
|
||||
@@ -207,7 +225,7 @@
|
||||
- (IBAction)thanksButton:(id)sender {
|
||||
|
||||
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
|
||||
[NSURL URLWithString:@"http://thanks.lhunath.com"]];
|
||||
[NSURL URLWithString:@"https://thanks.lhunath.com"]];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
@@ -226,10 +244,17 @@
|
||||
|
||||
- (MPSiteType)typeForSelectedSegment {
|
||||
|
||||
NSInteger selectedGeneratedIndex = self.generatedTypeControl.selectedSegmentIndex;
|
||||
NSInteger selectedGenerated1Index = self.generated1TypeControl.selectedSegmentIndex;
|
||||
NSInteger selectedGenerated2Index = self.generated2TypeControl.selectedSegmentIndex;
|
||||
NSInteger selectedStoredIndex = self.storedTypeControl.selectedSegmentIndex;
|
||||
|
||||
switch (selectedGeneratedIndex) {
|
||||
switch (selectedGenerated1Index) {
|
||||
case 0:
|
||||
return MPSiteTypeGeneratedPhrase;
|
||||
case 1:
|
||||
return MPSiteTypeGeneratedName;
|
||||
default:
|
||||
switch (selectedGenerated2Index) {
|
||||
case 0:
|
||||
return MPSiteTypeGeneratedMaximum;
|
||||
case 1:
|
||||
@@ -250,13 +275,26 @@
|
||||
case 1:
|
||||
return MPSiteTypeStoredDevicePrivate;
|
||||
default:
|
||||
Throw( @"unsupported selected type index: generated=%ld, stored=%ld", (long)selectedGeneratedIndex,
|
||||
(long)selectedStoredIndex );
|
||||
Throw( @"unsupported selected type index: generated1=%ld, generated2=%ld, stored=%ld",
|
||||
(long)selectedGenerated1Index, (long)selectedGenerated2Index, (long)selectedStoredIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)generatedSegmentIndexForType:(MPSiteType)type {
|
||||
- (NSInteger)generated1SegmentIndexForType:(MPSiteType)type {
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedPhrase:
|
||||
return 0;
|
||||
case MPSiteTypeGeneratedName:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)generated2SegmentIndexForType:(MPSiteType)type {
|
||||
|
||||
switch (type) {
|
||||
case MPSiteTypeGeneratedMaximum:
|
||||
|
||||
@@ -17,20 +17,11 @@
|
||||
//==============================================================================
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <StoreKit/StoreKit.h>
|
||||
|
||||
@class MPStoreProductCell;
|
||||
|
||||
@interface MPStoreViewController : PearlMutableStaticTableViewController
|
||||
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *generateLoginCell;
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *generateAnswersCell;
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *iOSIntegrationCell;
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *touchIDCell;
|
||||
@property(weak, nonatomic) IBOutlet MPStoreProductCell *fuelCell;
|
||||
@property(weak, nonatomic) IBOutlet UITableViewCell *loadingCell;
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *fuelMeterConstraint;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *fuelSpeedButton;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *fuelStatusLabel;
|
||||
@interface MPStoreViewController : UITableViewController
|
||||
|
||||
+ (NSString *)latestStoreFeatures;
|
||||
|
||||
@@ -39,7 +30,23 @@
|
||||
@interface MPStoreProductCell : UITableViewCell
|
||||
|
||||
@property(nonatomic) IBOutlet UILabel *priceLabel;
|
||||
@property(nonatomic) IBOutlet UILabel *titleLabel;
|
||||
@property(nonatomic) IBOutlet UILabel *descriptionLabel;
|
||||
@property(nonatomic) IBOutlet UIImageView *thumbnailView;
|
||||
@property(nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
|
||||
@property(nonatomic) IBOutlet UIView *purchasedIndicator;
|
||||
|
||||
@property(nonatomic, readonly) SKProduct *product;
|
||||
@property(nonatomic, readonly) NSInteger quantity;
|
||||
|
||||
- (void)updateWithProduct:(SKProduct *)product transaction:(SKPaymentTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
@interface MPStoreFuelProductCell : MPStoreProductCell
|
||||
|
||||
@property(weak, nonatomic) IBOutlet NSLayoutConstraint *fuelMeterConstraint;
|
||||
@property(weak, nonatomic) IBOutlet UIButton *fuelSpeedButton;
|
||||
@property(weak, nonatomic) IBOutlet UILabel *fuelStatusLabel;
|
||||
|
||||
@end
|
||||
|
||||
@@ -27,8 +27,9 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
@interface MPStoreViewController()<MPInAppDelegate>
|
||||
|
||||
@property(nonatomic, strong) NSNumberFormatter *currencyFormatter;
|
||||
@property(nonatomic, strong) NSArray *products;
|
||||
@property(nonatomic, strong) NSDictionary<NSString *, SKProduct *> *products;
|
||||
@property(nonatomic, strong) NSDictionary<NSString *, SKPaymentTransaction *> *transactions;
|
||||
@property(nonatomic, strong) NSMutableArray<NSArray *> *dataSource;
|
||||
|
||||
@end
|
||||
|
||||
@@ -43,7 +44,7 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
];
|
||||
NSInteger storeVersion = [[NSUserDefaults standardUserDefaults] integerForKey:@"storeVersion"];
|
||||
for (; storeVersion < [storeVersions count]; ++storeVersion)
|
||||
[features appendFormat:@"%@\n", storeVersions[storeVersion]];
|
||||
[features appendFormat:@"%@\n", storeVersions[(NSUInteger)storeVersion]];
|
||||
if (![features length])
|
||||
return nil;
|
||||
|
||||
@@ -57,14 +58,13 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
self.currencyFormatter = [NSNumberFormatter new];
|
||||
self.currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
|
||||
|
||||
self.tableView.tableHeaderView = [UIView new];
|
||||
self.tableView.tableFooterView = [UIView new];
|
||||
self.tableView.rowHeight = UITableViewAutomaticDimension;
|
||||
self.tableView.estimatedRowHeight = 400;
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
|
||||
self.dataSource = [@[ @[], @[ @"MPStoreCellSpinner", @"MPStoreCellFooter" ] ] mutableCopy];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
@@ -73,50 +73,44 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
self.tableView.contentInset = UIEdgeInsetsMake( 64, 0, 49, 0 );
|
||||
|
||||
[self reloadCellsHiding:self.allCellsBySection[0] showing:@[ self.loadingCell ]];
|
||||
[self.allCellsBySection[0] enumerateObjectsUsingBlock:^(MPStoreProductCell *cell, NSUInteger idx, BOOL *stop) {
|
||||
if ([cell isKindOfClass:[MPStoreProductCell class]]) {
|
||||
cell.purchasedIndicator.alpha = 0;
|
||||
[cell.activityIndicator stopAnimating];
|
||||
}
|
||||
}];
|
||||
|
||||
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPStoreViewController *self, NSNotification *note) {
|
||||
[self updateProducts];
|
||||
[self updateFuel];
|
||||
} );
|
||||
[[MPiOSAppDelegate get] registerProductsObserver:self];
|
||||
[self updateFuel];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
PearlRemoveNotificationObservers();
|
||||
[[MPiOSAppDelegate get] removeProductsObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (MPStoreProductCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
|
||||
MPStoreProductCell *cell = (MPStoreProductCell *)[super tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
if (indexPath.section == 0)
|
||||
cell.selectionStyle = [[MPiOSAppDelegate get] isFeatureUnlocked:[self productForCell:cell].productIdentifier]?
|
||||
UITableViewCellSelectionStyleNone: UITableViewCellSelectionStyleDefault;
|
||||
|
||||
if (cell.selectionStyle != UITableViewCellSelectionStyleNone) {
|
||||
cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.bounds];
|
||||
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRGBAHex:0x78DDFB33];
|
||||
}
|
||||
|
||||
return cell;
|
||||
return [self.dataSource count];
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
|
||||
return NO;
|
||||
return [self.dataSource[(NSUInteger)section] count];
|
||||
}
|
||||
|
||||
- (MPStoreProductCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
id content = self.dataSource[(NSUInteger)indexPath.section][(NSUInteger)indexPath.row];
|
||||
if ([content isKindOfClass:[SKProduct class]]) {
|
||||
SKProduct *product = content;
|
||||
MPStoreProductCell *cell;
|
||||
if ([product.productIdentifier isEqualToString:MPProductFuel])
|
||||
cell = [MPStoreFuelProductCell dequeueCellFromTableView:tableView indexPath:indexPath];
|
||||
else
|
||||
cell = [MPStoreProductCell dequeueCellFromTableView:tableView indexPath:indexPath];
|
||||
[cell updateWithProduct:product transaction:self.transactions[product.productIdentifier]];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
return [tableView dequeueReusableCellWithIdentifier:content forIndexPath:indexPath];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
@@ -128,14 +122,13 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
|
||||
MPStoreProductCell *cell = (MPStoreProductCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
if (cell.selectionStyle == UITableViewCellSelectionStyleNone)
|
||||
return;
|
||||
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
if (cell.selectionStyle != UITableViewCellSelectionStyleNone && [cell isKindOfClass:[MPStoreProductCell class]]) {
|
||||
MPStoreProductCell *productCell = (MPStoreProductCell *)cell;
|
||||
|
||||
SKProduct *product = [self productForCell:cell];
|
||||
if (product && ![[MPAppDelegate_Shared get] isFeatureUnlocked:product.productIdentifier])
|
||||
[[MPAppDelegate_Shared get] purchaseProductWithIdentifier:product.productIdentifier
|
||||
quantity:[self quantityForProductIdentifier:product.productIdentifier]];
|
||||
if (productCell.product && ![[MPAppDelegate_Shared get] isFeatureUnlocked:productCell.product.productIdentifier])
|
||||
[[MPAppDelegate_Shared get] purchaseProductWithIdentifier:productCell.product.productIdentifier quantity:productCell.quantity];
|
||||
}
|
||||
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
}
|
||||
@@ -146,7 +139,9 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
|
||||
NSUInteger fuelConsumption = [[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue];
|
||||
[MPiOSConfig get].developmentFuelConsumption = @((fuelConsumption + 1) % MPDevelopmentFuelConsumptionCount);
|
||||
[self updateProducts];
|
||||
|
||||
[self.tableView updateDataSource:self.dataSource toSections:nil reloadItems:@[ self.products[MPProductFuel] ]
|
||||
withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
- (IBAction)restorePurchases:(id)sender {
|
||||
@@ -165,41 +160,33 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
- (IBAction)sendThanks:(id)sender {
|
||||
|
||||
[[self dismissPopup].navigationController performSegueWithIdentifier:@"web" sender:
|
||||
[NSURL URLWithString:@"http://thanks.lhunath.com"]];
|
||||
[NSURL URLWithString:@"https://thanks.lhunath.com"]];
|
||||
}
|
||||
|
||||
#pragma mark - MPInAppDelegate
|
||||
|
||||
- (void)updateWithProducts:(NSArray *)products {
|
||||
- (void)updateWithProducts:(NSDictionary<NSString *, SKProduct *> *)products
|
||||
transactions:(NSDictionary<NSString *, SKPaymentTransaction *> *)transactions {
|
||||
|
||||
self.products = products;
|
||||
self.transactions = transactions;
|
||||
NSMutableArray *newDataSource = [NSMutableArray arrayWithCapacity:2];
|
||||
|
||||
[self updateProducts];
|
||||
}
|
||||
// Section 0: products
|
||||
[newDataSource addObject:[[products allValues] sortedArrayUsingComparator:
|
||||
^NSComparisonResult(SKProduct *p1, SKProduct *p2) {
|
||||
return [p1.productIdentifier compare:p2.productIdentifier];
|
||||
}]];
|
||||
NSArray *reloadProducts = [newDataSource[0] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:
|
||||
^BOOL(SKProduct *product, NSDictionary *bindings) {
|
||||
return self.transactions[product.productIdentifier] != nil;
|
||||
}]];
|
||||
|
||||
- (void)updateWithTransaction:(SKPaymentTransaction *)transaction {
|
||||
// Section 1: information cells
|
||||
[newDataSource addObject:@[ @"MPStoreCellFooter" ]];
|
||||
|
||||
MPStoreProductCell *cell = [self cellForProductIdentifier:transaction.payment.productIdentifier];
|
||||
if (!cell)
|
||||
return;
|
||||
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStatePurchasing:
|
||||
[cell.activityIndicator startAnimating];
|
||||
break;
|
||||
case SKPaymentTransactionStatePurchased:
|
||||
[cell.activityIndicator stopAnimating];
|
||||
break;
|
||||
case SKPaymentTransactionStateFailed:
|
||||
[cell.activityIndicator stopAnimating];
|
||||
break;
|
||||
case SKPaymentTransactionStateRestored:
|
||||
[cell.activityIndicator stopAnimating];
|
||||
break;
|
||||
case SKPaymentTransactionStateDeferred:
|
||||
[cell.activityIndicator startAnimating];
|
||||
break;
|
||||
}
|
||||
[self.tableView updateDataSource:self.dataSource toSections:newDataSource
|
||||
reloadItems:reloadProducts withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
@@ -216,60 +203,80 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (SKProduct *)productForCell:(MPStoreProductCell *)cell {
|
||||
@end
|
||||
|
||||
for (SKProduct *product in self.products)
|
||||
if ([self cellForProductIdentifier:product.productIdentifier] == cell)
|
||||
return product;
|
||||
@implementation MPStoreProductCell
|
||||
|
||||
- (void)updateWithProduct:(SKProduct *)product transaction:(SKPaymentTransaction *)transaction {
|
||||
|
||||
_product = product;
|
||||
|
||||
BOOL purchased = [[MPiOSAppDelegate get] isFeatureUnlocked:self.product.productIdentifier];
|
||||
self.selectionStyle = purchased? UITableViewCellSelectionStyleNone: UITableViewCellSelectionStyleDefault;
|
||||
self.selectedBackgroundView = self.selectionStyle == UITableViewCellSelectionStyleNone? nil: [[UIView alloc] initWithFrame:self.bounds];
|
||||
self.selectedBackgroundView.backgroundColor = [UIColor colorWithRGBAHex:0x78DDFB33];
|
||||
|
||||
self.purchasedIndicator.visible = purchased;
|
||||
self.priceLabel.text = purchased? @"": [self price];
|
||||
self.titleLabel.text = product.localizedTitle;
|
||||
self.descriptionLabel.text = product.localizedDescription;
|
||||
self.thumbnailView.image = [self productImage];
|
||||
|
||||
if (transaction && (transaction.transactionState == SKPaymentTransactionStateDeferred ||
|
||||
transaction.transactionState == SKPaymentTransactionStatePurchasing))
|
||||
[self.activityIndicator startAnimating];
|
||||
else
|
||||
[self.activityIndicator stopAnimating];
|
||||
}
|
||||
|
||||
- (UIImage *)productImage {
|
||||
|
||||
if ([MPProductGenerateLogins isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_generated_login"];
|
||||
if ([MPProductGenerateAnswers isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_generated_answers"];
|
||||
if ([MPProductOSIntegration isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_ios_integration"];
|
||||
if ([MPProductTouchID isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_touch_id"];
|
||||
if ([MPProductFuel isEqualToString:self.product.productIdentifier])
|
||||
return [UIImage imageNamed:@"thumb_fuel"];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (MPStoreProductCell *)cellForProductIdentifier:(NSString *)productIdentifier {
|
||||
- (NSString *)price {
|
||||
|
||||
if ([productIdentifier isEqualToString:MPProductGenerateLogins])
|
||||
return self.generateLoginCell;
|
||||
if ([productIdentifier isEqualToString:MPProductGenerateAnswers])
|
||||
return self.generateAnswersCell;
|
||||
if ([productIdentifier isEqualToString:MPProductOSIntegration])
|
||||
return self.iOSIntegrationCell;
|
||||
if ([productIdentifier isEqualToString:MPProductTouchID])
|
||||
return self.touchIDCell;
|
||||
if ([productIdentifier isEqualToString:MPProductFuel])
|
||||
return self.fuelCell;
|
||||
NSNumberFormatter *currencyFormatter = [NSNumberFormatter new];
|
||||
currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
|
||||
currencyFormatter.locale = self.product.priceLocale;
|
||||
|
||||
return nil;
|
||||
return [currencyFormatter stringFromNumber:@([self.product.price floatValue] * self.quantity)];
|
||||
}
|
||||
|
||||
- (void)updateProducts {
|
||||
- (NSInteger)quantity {
|
||||
|
||||
NSMutableArray *showCells = [NSMutableArray array];
|
||||
NSMutableArray *hideCells = [NSMutableArray array];
|
||||
[hideCells addObjectsFromArray:self.allCellsBySection[0]];
|
||||
[hideCells addObject:self.loadingCell];
|
||||
|
||||
for (SKProduct *product in self.products) {
|
||||
[self showCellForProductWithIdentifier:MPProductGenerateLogins ifProduct:product showingCells:showCells];
|
||||
[self showCellForProductWithIdentifier:MPProductGenerateAnswers ifProduct:product showingCells:showCells];
|
||||
[self showCellForProductWithIdentifier:MPProductOSIntegration ifProduct:product showingCells:showCells];
|
||||
[self showCellForProductWithIdentifier:MPProductTouchID ifProduct:product showingCells:showCells];
|
||||
[self showCellForProductWithIdentifier:MPProductFuel ifProduct:product showingCells:showCells];
|
||||
}
|
||||
|
||||
[hideCells removeObjectsInArray:showCells];
|
||||
[self reloadCellsHiding:hideCells showing:showCells];
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (void)updateFuel {
|
||||
@end
|
||||
|
||||
@implementation MPStoreFuelProductCell
|
||||
|
||||
- (void)updateWithProduct:(SKProduct *)product transaction:(SKPaymentTransaction *)transaction {
|
||||
|
||||
[super updateWithProduct:product transaction:transaction];
|
||||
|
||||
CGFloat weeklyFuelConsumption = [self weeklyFuelConsumption]; /* consume x fuel / week */
|
||||
[self.fuelSpeedButton setTitle:[self weeklyFuelConsumptionTitle] forState:UIControlStateNormal];
|
||||
|
||||
NSTimeInterval fuelSecondsElapsed = 0;
|
||||
CGFloat fuelRemaining = [[MPiOSConfig get].developmentFuelRemaining floatValue]; /* x fuel left */
|
||||
CGFloat fuelInvested = [[MPiOSConfig get].developmentFuelInvested floatValue]; /* x fuel left */
|
||||
NSDate *now = [NSDate date];
|
||||
NSTimeInterval fuelSecondsElapsed = -[[MPiOSConfig get].developmentFuelChecked timeIntervalSinceDate:now];
|
||||
if (fuelSecondsElapsed > 3600 || ![MPiOSConfig get].developmentFuelChecked) {
|
||||
NSDate *now = [NSDate date], *checked = [MPiOSConfig get].developmentFuelChecked;
|
||||
if (!checked || 3600 < (fuelSecondsElapsed = [now timeIntervalSinceDate:checked])) {
|
||||
NSTimeInterval weeksElapsed = fuelSecondsElapsed / (3600 * 24 * 7 /* 1 week */); /* x weeks elapsed */
|
||||
NSTimeInterval fuelConsumed = weeklyFuelConsumption * weeksElapsed;
|
||||
NSTimeInterval fuelConsumed = MIN( fuelRemaining, weeklyFuelConsumption * weeksElapsed );
|
||||
fuelRemaining -= fuelConsumed;
|
||||
fuelInvested += fuelConsumed;
|
||||
[MPiOSConfig get].developmentFuelChecked = now;
|
||||
@@ -277,54 +284,43 @@ PearlEnum( MPDevelopmentFuelConsumption,
|
||||
[MPiOSConfig get].developmentFuelInvested = @(fuelInvested);
|
||||
}
|
||||
|
||||
CGFloat fuelRatio = weeklyFuelConsumption == 0? 0: fuelRemaining / weeklyFuelConsumption; /* x weeks worth of fuel left */
|
||||
[self.fuelMeterConstraint updateConstant:MIN( 0.5f, fuelRatio - 0.5f ) * 160]; /* -80pt = 0 weeks left, 80pt = >=1 week left */
|
||||
self.fuelStatusLabel.text = strf( @"fuel left: %0.1f work hours\ninvested: %0.1f work hours", fuelRemaining, fuelInvested );
|
||||
CGFloat fuelRatio = weeklyFuelConsumption? fuelRemaining / weeklyFuelConsumption: 0; /* x weeks worth of fuel left */
|
||||
[self.fuelMeterConstraint updateConstant:MIN( 0.5f, fuelRatio - 0.5f ) * 160]; /* -80pt = 0 weeks left, +80pt = >=1 week left */
|
||||
self.fuelStatusLabel.text = strf( @"Fuel left: %0.1f work hours\nFunded: %0.1f work hours", fuelRemaining, fuelInvested );
|
||||
self.fuelStatusLabel.hidden = (fuelRemaining + fuelInvested) == 0;
|
||||
}
|
||||
|
||||
- (NSInteger)quantity {
|
||||
|
||||
return MAX( 1, (NSInteger)ceil( MP_FUEL_HOURLY_RATE * [self weeklyFuelConsumption] ) );
|
||||
}
|
||||
|
||||
- (CGFloat)weeklyFuelConsumption {
|
||||
|
||||
switch ((MPDevelopmentFuelConsumption)[[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue]) {
|
||||
case MPDevelopmentFuelConsumptionQuarterly:
|
||||
[self.fuelSpeedButton setTitle:@"1h / quarter" forState:UIControlStateNormal];
|
||||
return 1.f / 12 /* 12 weeks */;
|
||||
case MPDevelopmentFuelConsumptionMonthly:
|
||||
[self.fuelSpeedButton setTitle:@"1h / month" forState:UIControlStateNormal];
|
||||
return 1.f / 4 /* 4 weeks */;
|
||||
case MPDevelopmentFuelWeekly:
|
||||
[self.fuelSpeedButton setTitle:@"1h / week" forState:UIControlStateNormal];
|
||||
return 1.f;
|
||||
return 1.f /* 1 week */;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (void)showCellForProductWithIdentifier:(NSString *)productIdentifier ifProduct:(SKProduct *)product
|
||||
showingCells:(NSMutableArray *)showCells {
|
||||
- (NSString *)weeklyFuelConsumptionTitle {
|
||||
|
||||
if (![product.productIdentifier isEqualToString:productIdentifier])
|
||||
return;
|
||||
switch ((MPDevelopmentFuelConsumption)[[MPiOSConfig get].developmentFuelConsumption unsignedIntegerValue]) {
|
||||
case MPDevelopmentFuelConsumptionQuarterly:
|
||||
return @"1h / quarter";
|
||||
case MPDevelopmentFuelConsumptionMonthly:
|
||||
return @"1h / month";
|
||||
case MPDevelopmentFuelWeekly:
|
||||
return @"1h / week";
|
||||
}
|
||||
|
||||
MPStoreProductCell *cell = [self cellForProductIdentifier:productIdentifier];
|
||||
[showCells addObject:cell];
|
||||
|
||||
self.currencyFormatter.locale = product.priceLocale;
|
||||
BOOL purchased = [[MPiOSAppDelegate get] isFeatureUnlocked:productIdentifier];
|
||||
NSInteger quantity = [self quantityForProductIdentifier:productIdentifier];
|
||||
cell.priceLabel.text = purchased? @"": [self.currencyFormatter stringFromNumber:@([product.price floatValue] * quantity)];
|
||||
cell.purchasedIndicator.alpha = purchased? 1: 0;
|
||||
}
|
||||
|
||||
- (NSInteger)quantityForProductIdentifier:(NSString *)productIdentifier {
|
||||
|
||||
if ([productIdentifier isEqualToString:MPProductFuel])
|
||||
return (NSInteger)(MP_FUEL_HOURLY_RATE * [self weeklyFuelConsumption] + .5f);
|
||||
|
||||
return 1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPStoreProductCell
|
||||
@end
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
inf( @"Type selection will appear" );
|
||||
self.recommendedTipContainer.alpha = 0;
|
||||
self.recommendedTipContainer.visible = NO;
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
@@ -42,15 +42,13 @@
|
||||
|
||||
if ([[MPiOSConfig get].firstRun boolValue])
|
||||
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
|
||||
self.recommendedTipContainer.alpha = 1;
|
||||
self.recommendedTipContainer.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC) ), dispatch_get_main_queue(), ^{
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
self.recommendedTipContainer.alpha = 0;
|
||||
}];
|
||||
} );
|
||||
}
|
||||
PearlMainQueueAfter( 5, ^{
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
self.recommendedTipContainer.visible = NO;
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
|
||||
[super viewDidAppear:animated];
|
||||
@@ -89,11 +87,11 @@
|
||||
if ([selectedSite isKindOfClass:[MPGeneratedSiteEntity class]])
|
||||
counter = ((MPGeneratedSiteEntity *)selectedSite).counter;
|
||||
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
|
||||
PearlNotMainQueue( ^{
|
||||
NSString *typeContent = [MPAlgorithmDefault generatePasswordForSiteNamed:name ofType:cellType
|
||||
withCounter:counter usingKey:[MPiOSAppDelegate get].key];
|
||||
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
PearlMainQueue( ^{
|
||||
[(UITextField *)[[tableView cellForRowAtIndexPath:indexPath] viewWithTag:2] setText:typeContent];
|
||||
} );
|
||||
} );
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
|
||||
//==============================================================================
|
||||
|
||||
#import <Crashlytics/Answers.h>
|
||||
#import "MPUsersViewController.h"
|
||||
#import "MPEntities.h"
|
||||
#import "MPAvatarCell.h"
|
||||
@@ -58,6 +59,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
@implementation MPUsersViewController {
|
||||
NSString *_masterPasswordChoice;
|
||||
NSOperationQueue *_afterUpdates;
|
||||
__weak id _contextChangedObserver;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
@@ -76,7 +78,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
self.avatarCollectionView.allowsMultipleSelection = YES;
|
||||
[self.entryField addTarget:self action:@selector( textFieldEditingChanged: ) forControlEvents:UIControlEventEditingChanged];
|
||||
|
||||
self.preferencesTipContainer.alpha = 0;
|
||||
self.preferencesTipContainer.visible = NO;
|
||||
|
||||
[self setActive:YES animated:NO];
|
||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"tipped.thanks"])
|
||||
@@ -87,16 +89,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
self.userSelectionContainer.alpha = 0;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
PearlRemoveNotificationObservers();
|
||||
|
||||
[self.marqueeTipTimer invalidate];
|
||||
self.userSelectionContainer.visible = NO;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
@@ -126,8 +119,18 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
|
||||
if ([segue.identifier isEqualToString:@"web"])
|
||||
((MPWebViewController *)segue.destinationViewController).initialURL = [NSURL URLWithString:@"http://thanks.lhunath.com"];
|
||||
if ([segue.identifier isEqualToString:@"thanks"])
|
||||
((MPWebViewController *)segue.destinationViewController).initialURL =
|
||||
[NSURL URLWithString:@"https://thanks.lhunath.com"];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[self removeObservers];
|
||||
|
||||
[self.marqueeTipTimer invalidate];
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
@@ -220,8 +223,20 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
if (isNew) {
|
||||
user = [MPUserEntity insertNewObjectInContext:context];
|
||||
user.algorithm = MPAlgorithmDefault;
|
||||
user.defaultType = user.algorithm.defaultType;
|
||||
user.avatar = newUserAvatar;
|
||||
user.name = newUserName;
|
||||
|
||||
if ([[MPConfig get].sendInfo boolValue]) {
|
||||
#ifdef CRASHLYTICS
|
||||
[Answers logSignUpWithMethod:@"Manual"
|
||||
success:@YES
|
||||
customAttributes:@{
|
||||
@"algorithm": @(user.algorithm.version),
|
||||
@"avatar" : @(user.avatar),
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context
|
||||
@@ -258,6 +273,9 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
|
||||
// This isn't really in UITextFieldDelegate. We fake it from UITextFieldTextDidChangeNotification.
|
||||
- (void)textFieldEditingChanged:(UITextField *)textField {
|
||||
|
||||
if ([[textField.text lowercaseString] isEqualToString:@"hangtest"])
|
||||
[NSThread sleepForTimeInterval:10];
|
||||
|
||||
if (textField == self.entryField) {
|
||||
switch (self.activeUserState) {
|
||||
case MPActiveUserStateNone:
|
||||
@@ -367,7 +385,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
MPAvatarCell *userAvatar = [self selectedAvatar];
|
||||
userAvatar.spinnerActive = YES;
|
||||
if (!isNew && mainUser && [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *user = [MPUserEntity existingObjectWithID:mainUser.objectID inContext:context];
|
||||
MPUserEntity *user = [MPUserEntity existingObjectWithID:mainUser.permanentObjectID inContext:context];
|
||||
BOOL signedIn = [[MPiOSAppDelegate get] signInAsUser:user saveInContext:context usingMasterPassword:nil];
|
||||
|
||||
PearlMainQueue( ^{
|
||||
@@ -410,10 +428,10 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
|
||||
BOOL isNew = NO;
|
||||
MPUserEntity *user = [self userForAvatar:avatarCell inContext:mainContext isNew:&isNew];
|
||||
NSManagedObjectID *userID = user.objectID;
|
||||
if (isNew || !user)
|
||||
return;
|
||||
|
||||
NSManagedObjectID *userID = user.permanentObjectID;
|
||||
[PearlSheet showSheetWithTitle:user.name
|
||||
viewStyle:UIActionSheetStyleBlackTranslucent
|
||||
initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) {
|
||||
@@ -464,25 +482,23 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
- (void)deleteUser:(NSManagedObjectID *)userID {
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity
|
||||
*user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||
if (!user_)
|
||||
MPUserEntity *user = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||
if (!user)
|
||||
return;
|
||||
|
||||
[context deleteObject:user_];
|
||||
[context deleteObject:user];
|
||||
[context saveToStore];
|
||||
[self reloadUsers]; // I do NOT understand why our ObjectsDidChangeNotification isn't firing on saveToStore.
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)resetUser:(NSManagedObjectID *)userID avatar:(MPAvatarCell *)avatarCell {
|
||||
|
||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||
if (!user_)
|
||||
MPUserEntity *user = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||
if (!user)
|
||||
return;
|
||||
|
||||
[[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
|
||||
[[MPiOSAppDelegate get] changeMasterPasswordFor:user saveInContext:context didResetBlock:^{
|
||||
PearlMainQueue( ^{
|
||||
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
|
||||
[self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
|
||||
@@ -497,25 +513,25 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPUsersThanksTip)
|
||||
self.thanksTipContainer.alpha = 1;
|
||||
self.thanksTipContainer.visible = YES;
|
||||
if (showTips & MPUsersAvatarTip)
|
||||
self.avatarTipContainer.alpha = 1;
|
||||
self.avatarTipContainer.visible = YES;
|
||||
if (showTips & MPUsersMasterPasswordTip)
|
||||
self.entryTipContainer.alpha = 1;
|
||||
self.entryTipContainer.visible = YES;
|
||||
if (showTips & MPUsersPreferencesTip)
|
||||
self.preferencesTipContainer.alpha = 1;
|
||||
self.preferencesTipContainer.visible = YES;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished)
|
||||
PearlMainQueueAfter( 5, ^{
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
if (showTips & MPUsersThanksTip)
|
||||
self.thanksTipContainer.alpha = 0;
|
||||
self.thanksTipContainer.visible = NO;
|
||||
if (showTips & MPUsersAvatarTip)
|
||||
self.avatarTipContainer.alpha = 0;
|
||||
self.avatarTipContainer.visible = NO;
|
||||
if (showTips & MPUsersMasterPasswordTip)
|
||||
self.entryTipContainer.alpha = 0;
|
||||
self.entryTipContainer.visible = NO;
|
||||
if (showTips & MPUsersPreferencesTip)
|
||||
self.preferencesTipContainer.alpha = 0;
|
||||
self.preferencesTipContainer.visible = NO;
|
||||
}];
|
||||
} );
|
||||
}];
|
||||
@@ -538,14 +554,14 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
return;
|
||||
|
||||
[UIView animateWithDuration:timer? 0.5f: 0 animations:^{
|
||||
self.marqueeButton.alpha = 0;
|
||||
self.marqueeButton.visible = NO;
|
||||
} completion:^(BOOL finished) {
|
||||
if (!finished)
|
||||
return;
|
||||
|
||||
[self.marqueeButton setTitle:nextMarqueeString forState:UIControlStateNormal];
|
||||
[UIView animateWithDuration:timer? 0.5f: 0 animations:^{
|
||||
self.marqueeButton.alpha = 0.5f;
|
||||
self.marqueeButton.visible = YES;
|
||||
}];
|
||||
}];
|
||||
}
|
||||
@@ -588,8 +604,6 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
|
||||
- (void)updateAvatarVisibility {
|
||||
|
||||
self.previousAvatarButton.alpha = 0;
|
||||
self.nextAvatarButton.alpha = 0;
|
||||
for (NSIndexPath *indexPath in self.avatarCollectionView.indexPathsForVisibleItems) {
|
||||
MPAvatarCell *cell = (MPAvatarCell *)[self.avatarCollectionView cellForItemAtIndexPath:indexPath];
|
||||
[self updateVisibilityForAvatar:cell atIndexPath:indexPath animated:NO];
|
||||
@@ -633,10 +647,10 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
CGFloat visibility = MAX( 0, MIN( 1, 1 - ABS( current / (max / 2) - 1 ) ) );
|
||||
[cell setVisibility:visibility animated:animated];
|
||||
|
||||
if (cell.newUser) {
|
||||
self.previousAvatarButton.alpha = cell.mode == MPAvatarModeRaisedAndActive? visibility * 0.7f: 0;
|
||||
self.nextAvatarButton.alpha = cell.mode == MPAvatarModeRaisedAndActive? visibility * 0.7f: 0;
|
||||
}
|
||||
[UIView animateWithDuration:animated? 0.3f: 0 animations:^{
|
||||
self.nextAvatarButton.visible = self.previousAvatarButton.visible = cell.newUser && cell.mode == MPAvatarModeRaisedAndActive;
|
||||
self.nextAvatarButton.alpha = self.previousAvatarButton.alpha = visibility * 0.7f;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)afterUpdatesMainQueue:(void ( ^ )(void))block {
|
||||
@@ -646,18 +660,24 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)registerObservers {
|
||||
- (void)removeObservers {
|
||||
|
||||
[self removeKeyPathObservers];
|
||||
PearlRemoveNotificationObservers();
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:_contextChangedObserver];
|
||||
}
|
||||
|
||||
- (void)registerObservers {
|
||||
|
||||
[self removeObservers];
|
||||
[self observeKeyPath:@"avatarCollectionView.contentOffset" withBlock:
|
||||
^(id from, id to, NSKeyValueChange cause, MPUsersViewController *_self) {
|
||||
[_self updateAvatarVisibility];
|
||||
}];
|
||||
|
||||
PearlRemoveNotificationObservers();
|
||||
PearlAddNotificationObserver( UIApplicationDidEnterBackgroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
self.userSelectionContainer.alpha = 0;
|
||||
self.userSelectionContainer.visible = NO;
|
||||
} );
|
||||
PearlAddNotificationObserver( UIApplicationWillEnterForegroundNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
@@ -666,7 +686,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
PearlAddNotificationObserver( UIApplicationDidBecomeActiveNotification, nil, [NSOperationQueue mainQueue],
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
[UIView animateWithDuration:0.5f animations:^{
|
||||
self.userSelectionContainer.alpha = 1;
|
||||
self.userSelectionContainer.visible = YES;
|
||||
}];
|
||||
} );
|
||||
PearlAddNotificationObserver( UIKeyboardWillShowNotification, nil, [NSOperationQueue mainQueue],
|
||||
@@ -676,36 +696,31 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
[self.keyboardHeightConstraint updateConstant:keyboardHeight];
|
||||
} );
|
||||
|
||||
NSManagedObjectContext *mainContext = [MPiOSAppDelegate managedObjectContextForMainThreadIfReady];
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.avatarCollectionView.alpha = mainContext? 1: 0;
|
||||
}];
|
||||
if (mainContext && self.storeLoadingActivity.isAnimating)
|
||||
[self.storeLoadingActivity stopAnimating];
|
||||
if (!mainContext && !self.storeLoadingActivity.isAnimating)
|
||||
[self.storeLoadingActivity startAnimating];
|
||||
if ((_contextChangedObserver = [MPiOSAppDelegate managedObjectContextChanged:^(NSDictionary<NSManagedObjectID *, NSString *> *affectedObjects) {
|
||||
if ([[[affectedObjects allKeys] filteredArrayUsingPredicate:
|
||||
[NSPredicate predicateWithBlock:^BOOL(NSManagedObjectID *objectID, NSDictionary *bindings) {
|
||||
return [objectID.entity.name isEqualToString:NSStringFromClass( [MPUserEntity class] )];
|
||||
}]] count])
|
||||
[self reloadUsers];
|
||||
}]))
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.avatarCollectionView.visible = YES;
|
||||
[self.storeLoadingActivity stopAnimating];
|
||||
}];
|
||||
else
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.avatarCollectionView.visible = NO;
|
||||
[self.storeLoadingActivity startAnimating];
|
||||
}];
|
||||
|
||||
if (mainContext)
|
||||
PearlAddNotificationObserver( NSManagedObjectContextObjectsDidChangeNotification, mainContext, nil,
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
NSSet *insertedObjects = note.userInfo[NSInsertedObjectsKey];
|
||||
NSSet *deletedObjects = note.userInfo[NSDeletedObjectsKey];
|
||||
if ([[NSSetUnion( insertedObjects, deletedObjects )
|
||||
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
|
||||
return [evaluatedObject isKindOfClass:[MPUserEntity class]];
|
||||
}]] count])
|
||||
[self reloadUsers];
|
||||
} );
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresWillChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
self.userIDs = nil;
|
||||
} );
|
||||
PearlAddNotificationObserver( NSPersistentStoreCoordinatorStoresDidChangeNotification, [MPiOSAppDelegate get].storeCoordinator, nil,
|
||||
^(MPUsersViewController *self, NSNotification *note) {
|
||||
PearlMainQueue( ^{
|
||||
[self registerObservers];
|
||||
[self reloadUsers];
|
||||
} );
|
||||
[self registerObservers];
|
||||
[self reloadUsers];
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -720,13 +735,13 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
];
|
||||
NSArray *users = [mainContext executeFetchRequest:fetchRequest error:&error];
|
||||
if (!users) {
|
||||
err( @"Failed to load users: %@", [error fullDescription] );
|
||||
MPError( error, @"Failed to load users." );
|
||||
self.userIDs = nil;
|
||||
}
|
||||
|
||||
NSMutableArray *userIDs = [NSMutableArray arrayWithCapacity:[users count]];
|
||||
for (MPUserEntity *user in users)
|
||||
[userIDs addObject:user.objectID];
|
||||
[userIDs addObject:user.permanentObjectID];
|
||||
self.userIDs = userIDs;
|
||||
}])
|
||||
self.userIDs = nil;
|
||||
@@ -754,7 +769,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
NSManagedObjectID *selectUserID = [MPiOSAppDelegate get].activeUserOID;
|
||||
if (!selectUserID)
|
||||
selectUserID = [self selectedUserInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]
|
||||
isNew:&isNew].objectID;
|
||||
isNew:&isNew].permanentObjectID;
|
||||
[self.avatarCollectionView reloadData];
|
||||
|
||||
NSUInteger selectedAvatarItem = isNew? [_userIDs count]: selectUserID? [_userIDs indexOfObject:selectUserID]: NSNotFound;
|
||||
@@ -763,7 +778,7 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
|
||||
|
||||
[UIView animateWithDuration:0.3f animations:^{
|
||||
self.userSelectionContainer.alpha = 1;
|
||||
self.userSelectionContainer.visible = YES;
|
||||
}];
|
||||
} );
|
||||
}
|
||||
@@ -828,8 +843,8 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
switch (activeUserState) {
|
||||
case MPActiveUserStateNone: {
|
||||
self.avatarCollectionView.scrollEnabled = YES;
|
||||
self.entryContainer.alpha = 0;
|
||||
self.footerContainer.alpha = 1;
|
||||
self.entryContainer.visible = NO;
|
||||
self.footerContainer.visible = YES;
|
||||
break;
|
||||
}
|
||||
case MPActiveUserStateLogin:
|
||||
@@ -837,15 +852,15 @@ referenceSizeForFooterInSection:(NSInteger)section {
|
||||
case MPActiveUserStateMasterPasswordChoice:
|
||||
case MPActiveUserStateMasterPasswordConfirmation: {
|
||||
self.avatarCollectionView.scrollEnabled = NO;
|
||||
self.entryContainer.alpha = 1;
|
||||
self.footerContainer.alpha = 1;
|
||||
self.entryContainer.visible = YES;
|
||||
self.footerContainer.visible = YES;
|
||||
requestFirstResponder = YES;
|
||||
break;
|
||||
}
|
||||
case MPActiveUserStateMinimized: {
|
||||
self.avatarCollectionView.scrollEnabled = NO;
|
||||
self.entryContainer.alpha = 0;
|
||||
self.footerContainer.alpha = 0;
|
||||
self.entryContainer.visible = NO;
|
||||
self.footerContainer.visible = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (!self.initialURL)
|
||||
self.initialURL = [NSURL URLWithString:@"http://masterpasswordapp.com"];
|
||||
self.initialURL = [NSURL URLWithString:@"https://ssl.masterpasswordapp.com"];
|
||||
|
||||
self.webNavigationItem.title = self.initialURL.host;
|
||||
|
||||
self.webView.alpha = 0;
|
||||
self.webView.visible = NO;
|
||||
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:self.initialURL]];
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
- (void)webViewDidFinishLoad:(UIWebView *)webView {
|
||||
|
||||
[UIView animateWithDuration:0.3 animations:^{
|
||||
self.webView.alpha = 1;
|
||||
self.webView.visible = YES;
|
||||
}];
|
||||
|
||||
[self.webNavigationItem setLeftBarButtonItem:[webView canGoBack]? [[UIBarButtonItem alloc]
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
#import "MPiOSAppDelegate.h"
|
||||
#import "MPAppDelegate_Key.h"
|
||||
#import "MPAppDelegate_Store.h"
|
||||
#import "IASKSettingsReader.h"
|
||||
#import "MPStoreViewController.h"
|
||||
|
||||
#define MP_HANG_TIME_MAIN 3 // s
|
||||
|
||||
@interface MPiOSAppDelegate()<UIDocumentInteractionControllerDelegate>
|
||||
|
||||
@property(nonatomic, strong) UIDocumentInteractionController *interactionController;
|
||||
@@ -53,16 +54,15 @@
|
||||
NSString *crashlyticsAPIKey = [self crashlyticsAPIKey];
|
||||
if ([crashlyticsAPIKey length]) {
|
||||
inf( @"Initializing Crashlytics" );
|
||||
#if defined (DEBUG) || defined (ADHOC)
|
||||
#if DEBUG
|
||||
[Crashlytics sharedInstance].debugMode = YES;
|
||||
#endif
|
||||
[[Crashlytics sharedInstance] setUserIdentifier:[PearlKeyChain deviceIdentifier]];
|
||||
[[Crashlytics sharedInstance] setObjectValue:[PearlKeyChain deviceIdentifier] forKey:@"deviceIdentifier"];
|
||||
[[Crashlytics sharedInstance] setUserName:@"Anonymous"];
|
||||
[[Crashlytics sharedInstance] setObjectValue:@"Anonymous" forKey:@"username"];
|
||||
[Crashlytics startWithAPIKey:crashlyticsAPIKey];
|
||||
[[PearlLogger get] registerListener:^BOOL(PearlLogMessage *message) {
|
||||
PearlLogLevel level = PearlLogLevelInfo;
|
||||
PearlLogLevel level = PearlLogLevelWarn;
|
||||
if ([[MPConfig get].sendInfo boolValue])
|
||||
level = PearlLogLevelDebug;
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
[Crashlytics sharedInstance].version, [PearlInfoPlist get].CFBundleName, [PearlInfoPlist get].CFBundleVersion );
|
||||
}
|
||||
#endif
|
||||
|
||||
[self installHangDetector];
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"During Analytics Setup: %@", exception );
|
||||
@@ -83,24 +85,9 @@
|
||||
PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) {
|
||||
[self updateConfigKey:note.object];
|
||||
} );
|
||||
// PearlAddNotificationObserver( kIASKAppSettingChanged, nil, nil, ^(id self, NSNotification *note) {
|
||||
// [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note.object];
|
||||
// } );
|
||||
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
|
||||
} );
|
||||
|
||||
#ifdef ADHOC
|
||||
[PearlAlert showAlertWithTitle:@"Welcome, tester!" message:
|
||||
@"Thank you for taking the time to test Master Password.\n\n"
|
||||
@"Please provide any feedback, however minor it may seem, via the Feedback action item accessible from the top right.\n\n"
|
||||
@"Contact me directly at:\n"
|
||||
@"lhunath@lyndir.com\n"
|
||||
@"Or report detailed issues at:\n"
|
||||
@"https://youtrack.lyndir.com\n"
|
||||
viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:nil
|
||||
cancelTitle:nil otherTitles:[PearlStrings get].commonButtonOkay, nil];
|
||||
#endif
|
||||
}
|
||||
@catch (id exception) {
|
||||
err( @"During Config Test: %@", exception );
|
||||
@@ -156,6 +143,31 @@
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)installHangDetector {
|
||||
|
||||
__block NSDate *latestPing = [NSDate date];
|
||||
__block __weak VoidBlock wPingOp, wPongOp;
|
||||
|
||||
VoidBlock pingOp = ^{
|
||||
latestPing = [NSDate date];
|
||||
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC ), dispatch_get_main_queue(), wPingOp );
|
||||
}, pongOp = ^{
|
||||
NSTimeInterval hangTime = -[latestPing timeIntervalSinceNow];
|
||||
if (hangTime > MP_HANG_TIME_MAIN) {
|
||||
MPError( [NSError errorWithDomain:MPErrorDomain code:MPErrorHangCode userInfo:@{
|
||||
@"time": @(hangTime)
|
||||
}], @"Timeout waiting for main thread after %fs.", hangTime );
|
||||
}
|
||||
else
|
||||
dbg( @"hangTime=%f", hangTime );
|
||||
|
||||
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, NSEC_PER_SEC ), dispatch_get_global_queue( QOS_CLASS_BACKGROUND, 0 ), wPongOp );
|
||||
};
|
||||
|
||||
(wPingOp = pingOp)();
|
||||
(wPongOp = pongOp)();
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
|
||||
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
|
||||
|
||||
@@ -164,26 +176,25 @@
|
||||
return NO;
|
||||
|
||||
// Arbitrary URL to mpsites data.
|
||||
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
|
||||
NSError *error;
|
||||
NSURLResponse *response;
|
||||
NSData *importedSitesData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url]
|
||||
returningResponse:&response error:&error];
|
||||
if (error)
|
||||
err( @"While reading imported sites from %@: %@", url, [error fullDescription] );
|
||||
if (!importedSitesData) {
|
||||
[PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@", [error localizedDescription]?: error )];
|
||||
return;
|
||||
}
|
||||
[[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:
|
||||
^(NSData *importedSitesData, NSURLResponse *response, NSError *error) {
|
||||
if (error)
|
||||
MPError( error, @"While reading imported sites from %@.", url );
|
||||
|
||||
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
|
||||
if (!importedSitesString) {
|
||||
[PearlAlert showError:@"Master Password couldn't understand the import file."];
|
||||
return;
|
||||
}
|
||||
if (!importedSitesData) {
|
||||
[PearlAlert showError:strf( @"Master Password couldn't read the import sites.\n\n%@",
|
||||
[error localizedDescription]?: error )];
|
||||
return;
|
||||
}
|
||||
|
||||
[self importSites:importedSitesString];
|
||||
} );
|
||||
NSString *importedSitesString = [[NSString alloc] initWithData:importedSitesData encoding:NSUTF8StringEncoding];
|
||||
if (!importedSitesString) {
|
||||
[PearlAlert showError:@"Master Password couldn't understand the import file."];
|
||||
return;
|
||||
}
|
||||
|
||||
[self importSites:importedSitesString];
|
||||
}] resume];
|
||||
|
||||
return YES;
|
||||
}
|
||||
@@ -199,57 +210,33 @@
|
||||
|
||||
PearlOverlay *activityOverlay = [PearlOverlay showProgressOverlayWithTitle:@"Importing"];
|
||||
MPImportResult result = [self importSites:importedSitesString askImportPassword:^NSString *(NSString *userName) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
dispatch_group_t importPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter( importPasswordGroup );
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
return PearlAwait( ^(void (^setResult)(id)) {
|
||||
[PearlAlert showAlertWithTitle:@"Import File's Master Password"
|
||||
message:strf( @"%@'s export was done using a different master password.\n"
|
||||
@"Enter that master password to unlock the exported data.", userName )
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
return;
|
||||
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
@finally {
|
||||
dispatch_group_leave( importPasswordGroup );
|
||||
}
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
setResult( nil );
|
||||
else
|
||||
setResult( [alert_ textFieldAtIndex:0].text );
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Unlock Import", nil];
|
||||
} );
|
||||
dispatch_group_wait( importPasswordGroup, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return masterPassword;
|
||||
} askUserPassword:^NSString *(NSString *userName, NSUInteger importCount, NSUInteger deleteCount) {
|
||||
__block NSString *masterPassword = nil;
|
||||
|
||||
dispatch_group_t userPasswordGroup = dispatch_group_create();
|
||||
dispatch_group_enter( userPasswordGroup );
|
||||
dispatch_async( dispatch_get_main_queue(), ^{
|
||||
return PearlAwait( (id)^(void (^setResult)(id)) {
|
||||
[PearlAlert showAlertWithTitle:strf( @"Master Password for\n%@", userName )
|
||||
message:strf( @"Imports %lu sites, overwriting %lu.",
|
||||
(unsigned long)importCount, (unsigned long)deleteCount )
|
||||
viewStyle:UIAlertViewStyleSecureTextInput
|
||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert_, NSInteger buttonIndex_) {
|
||||
@try {
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
return;
|
||||
|
||||
masterPassword = [alert_ textFieldAtIndex:0].text;
|
||||
}
|
||||
@finally {
|
||||
dispatch_group_leave( userPasswordGroup );
|
||||
}
|
||||
if (buttonIndex_ == [alert_ cancelButtonIndex])
|
||||
setResult( nil );
|
||||
else
|
||||
setResult( [alert_ textFieldAtIndex:0].text );
|
||||
}
|
||||
cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:@"Import", nil];
|
||||
} );
|
||||
dispatch_group_wait( userPasswordGroup, DISPATCH_TIME_FOREVER );
|
||||
|
||||
return masterPassword;
|
||||
}];
|
||||
|
||||
switch (result) {
|
||||
@@ -283,6 +270,44 @@
|
||||
if (![[MPiOSConfig get].rememberLogin boolValue])
|
||||
[self signOutAnimated:NO];
|
||||
|
||||
// self.task = [application beginBackgroundTaskWithExpirationHandler:^{
|
||||
// [application endBackgroundTask:self.task];
|
||||
// dbg( @"background expiring" );
|
||||
// }];
|
||||
// PearlNotMainQueueOperation( ^{
|
||||
// NSString *pbstring = [UIPasteboard generalPasteboard].string;
|
||||
// while (YES) {
|
||||
// NSString *newString = [UIPasteboard generalPasteboard].string;
|
||||
// if (![newString isEqualToString:pbstring]) {
|
||||
// dbg( @"pasteboard changed to: %@", newString );
|
||||
// pbstring = newString;
|
||||
// NSURL *url = [NSURL URLWithString:pbstring];
|
||||
// if (url) {
|
||||
// NSString *siteName = [url host];
|
||||
// }
|
||||
// MPKey *key = [MPiOSAppDelegate get].key;
|
||||
// if (key)
|
||||
// [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||
// NSFetchRequest<MPSiteEntity *>
|
||||
// *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPSiteEntity class] )];
|
||||
// fetchRequest.sortDescriptors = @[
|
||||
// [[NSSortDescriptor alloc] initWithKey:NSStringFromSelector( @selector( lastUsed ) ) ascending:NO]
|
||||
// ];
|
||||
// fetchRequest.fetchBatchSize = 2;
|
||||
// fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name LIKE[cd] %@) AND user == %@", siteName,
|
||||
// [[MPiOSAppDelegate get] activeUserOID]];
|
||||
// NSError *error = nil;
|
||||
// NSArray<MPSiteEntity *> *results = [fetchRequest execute:&error];
|
||||
// dbg( @"site search, error: %@, results:\n%@", error, results );
|
||||
// if ([results count]) {
|
||||
// [UIPasteboard generalPasteboard].string = [[results firstObject] resolvePasswordUsingKey:key];
|
||||
// }
|
||||
// }];
|
||||
// }
|
||||
// [NSThread sleepForTimeInterval:5];
|
||||
// }
|
||||
// } );
|
||||
|
||||
[super applicationDidEnterBackground:application];
|
||||
}
|
||||
|
||||
@@ -483,7 +508,7 @@
|
||||
NSError *error = nil;
|
||||
if (![[exportedSites dataUsingEncoding:NSUTF8StringEncoding]
|
||||
writeToURL:exportURL options:NSDataWritingFileProtectionComplete error:&error])
|
||||
err( @"Failed to write export data to URL %@: %@", exportURL, [error fullDescription] );
|
||||
MPError( error, @"Failed to write export data to URL %@.", exportURL );
|
||||
else {
|
||||
self.interactionController = [UIDocumentInteractionController interactionControllerWithURL:exportURL];
|
||||
self.interactionController.UTI = @"com.lyndir.masterpassword.sites";
|
||||
@@ -574,7 +599,7 @@
|
||||
static NSDictionary *crashlyticsInfo = nil;
|
||||
if (crashlyticsInfo == nil)
|
||||
crashlyticsInfo = [[NSDictionary alloc] initWithContentsOfURL:
|
||||
[[NSBundle mainBundle] URLForResource:@"Crashlytics" withExtension:@"plist"]];
|
||||
[[NSBundle mainBundle] URLForResource:@"Fabric" withExtension:@"plist"]];
|
||||
|
||||
return crashlyticsInfo;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© 2011-2016 Lyndir</string>
|
||||
<string>© 2011-2017</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Exo2.0-Bold.otf</string>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<array>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Enable this setting to send us carefully anonymized information to help us diagnose and resolve issues you might experience in the future.</string>
|
||||
<string>Enable this setting to send carefully anonymized information to help us diagnose and resolve any issues you encounter in a future update.</string>
|
||||
<key>Title</key>
|
||||
<string></string>
|
||||
<key>Type</key>
|
||||
@@ -50,7 +50,7 @@
|
||||
<key>Key</key>
|
||||
<string>sendInfo</string>
|
||||
<key>DefaultValue</key>
|
||||
<false/>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -339,7 +339,7 @@ mpw-tests() {
|
||||
### TARGETS
|
||||
|
||||
haslib() {
|
||||
! LC_ALL=C cc -l"$1" 2>&1 | grep -q 'library not found'
|
||||
return cc -l"$1" -x c -o /dev/null - <<< 'int main() { return 0; }'
|
||||
}
|
||||
cc() {
|
||||
if hash llvm-gcc 2>/dev/null; then
|
||||
|
||||
@@ -5,4 +5,4 @@ set -e
|
||||
cd "${BASH_SOURCE%/*}"
|
||||
|
||||
rm -vfr lib/*/{.unpacked,.patched,src} lib/include
|
||||
rm -vfr {core,cli}{*.o,*.dSYM} mpw mpw-bench mpw-tests
|
||||
rm -vfr {core,cli,.}/{*.o,*.dSYM} mpw mpw-bench mpw-tests
|
||||
|
||||
@@ -11,19 +11,24 @@ mpwArchive=mpw-$commit.tar.gz
|
||||
[[ -e $mpwArchive ]] && echo >&2 "WARNING: $mpwArchive already exists. Will overwrite."
|
||||
read -n1 -p "Will prepare and release $mpwArchive. Press a key to continue or ^C to abort."
|
||||
|
||||
echo "Cleaning .."
|
||||
( git clean -ffdx . && cd core && git clean -ffdx . )
|
||||
|
||||
echo "Creating archive $mpwArchive .."
|
||||
git ls-files -z . | xargs -0 tar -Lcvzf "$mpwArchive"
|
||||
|
||||
echo "Creating archive signature $mpwArchive.sig .."
|
||||
gpg --detach-sign "$mpwArchive"
|
||||
|
||||
echo "Installing archive and signature in current site .."
|
||||
cd ../../public/site/current
|
||||
ln -sf "../../../platform-independent/cli-c/$mpwArchive"; [[ -e $_ ]]
|
||||
ln -sf "../../../platform-independent/cli-c/$mpwArchive.sig"; [[ -e $_ ]]
|
||||
mv "../../../platform-independent/cli-c/$mpwArchive" .; [[ -e $_ ]]
|
||||
mv "../../../platform-independent/cli-c/$mpwArchive.sig" .; [[ -e $_ ]]
|
||||
ln -sf "$mpwArchive" "masterpassword-cli.tar.gz"; [[ -e $_ ]]
|
||||
ln -sf "$mpwArchive.sig" "masterpassword-cli.tar.gz.sig"; [[ -e $_ ]]
|
||||
|
||||
echo
|
||||
echo "Done. Don't forget to publish the site."
|
||||
echo "package: $mpwArchive"
|
||||
echo "signature: $mpwArchive.sig"
|
||||
echo "Done. Ready to publish the site."
|
||||
echo " package: $mpwArchive"
|
||||
echo " signature: $mpwArchive.sig"
|
||||
echo " url: https://ssl.masterpasswordapp.com/$mpwArchive"
|
||||
|
||||
@@ -1 +1 @@
|
||||
mpw-2.5-cli-1-0-gb01e370f.tar.gz
|
||||
mpw-2.5-cli-2-0-g3ed6b937.tar.gz
|
||||
@@ -1 +1 @@
|
||||
mpw-2.5-cli-1-0-gb01e370f.tar.gz.sig
|
||||
mpw-2.5-cli-2-0-g3ed6b937.tar.gz.sig
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.1-cli1-0-g10f1001.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.1-cli1-0-g10f1001.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.1-cli1-0-g10f1001.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.1-cli2-2-g82c96dd.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.1-cli2-2-g82c96dd.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.1-cli2-2-g82c96dd.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.1-cli3-0-g438daf2.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.1-cli3-0-g438daf2.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.1-cli3-0-g438daf2.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.1-cli4-0-gf6b2287.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.1-cli4-0-gf6b2287.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.1-cli4-0-gf6b2287.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.5-cli-1-0-gb01e370f.tar.gz
|
||||
BIN
public/site/2013-05/mpw-2.5-cli-1-0-gb01e370f.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.5-cli-1-0-gb01e370f.tar.gz
Normal file
Binary file not shown.
@@ -1 +0,0 @@
|
||||
../../../platform-independent/cli-c/mpw-2.5-cli-1-0-gb01e370f.tar.gz.sig
|
||||
BIN
public/site/2013-05/mpw-2.5-cli-1-0-gb01e370f.tar.gz.sig
Normal file
BIN
public/site/2013-05/mpw-2.5-cli-1-0-gb01e370f.tar.gz.sig
Normal file
Binary file not shown.
BIN
public/site/2013-05/mpw-2.5-cli-2-0-g3ed6b937.tar.gz
Normal file
BIN
public/site/2013-05/mpw-2.5-cli-2-0-g3ed6b937.tar.gz
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user