├── .gitignore
├── LICENSE.txt
├── README-DateTimeUtils.md
├── README-RSAUtils.md
├── README-SnowflakeIdGenerator.md
├── README.md
├── RELEASE-NOTES.md
├── SwiftUtils.playground
├── Contents.swift
└── contents.xcplayground
├── SwiftUtils.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcuserdata
│ └── btnguyen.xcuserdatad
│ └── xcschemes
│ ├── SwiftUtils.xcscheme
│ └── xcschememanagement.plist
├── SwiftUtils.xcworkspace
└── contents.xcworkspacedata
├── SwiftUtils
├── DateTimeUtils.swift
├── Info.plist
├── RSAUtils.swift
├── SnowflakeIdGenerator.swift
└── SyncUtils.swift
├── SwiftUtilsPlayground
├── SwiftUtilsPlayground.xcodeproj
│ └── project.pbxproj
└── SwiftUtilsPlayground
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ ├── SwiftUtilsPlayground.entitlements
│ └── ViewController.swift
└── SwiftUtilsTests
├── Info.plist
└── SwiftUtilsTests.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Thanh Ba Nguyen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README-DateTimeUtils.md:
--------------------------------------------------------------------------------
1 | DateTimeUtils
2 | =============
3 |
4 | Datetime utility class.
5 |
6 |
7 | Functions
8 | ---------
9 |
10 | - `static currentUnixTimestampMillisec() -> UInt64`: get current UNIX timestamp in milliseconds.
11 | - `static currentUnixTimestampSecond() -> UInt64`: get current UNIX timestamp in seconds.
12 | - `static waitTillNextMillisec(currentMillisec: UInt64) -> UInt64`: wait until clock moves to the next millisecond (return the "next" millisecond).
13 | - `static waitTillNextSecond(currentSecond: UInt64) -> UInt64`: wait until clock moves to the next second (return the "next" second).
14 | - `static startOfSecond(date: Date) -> Date`: reset to start of the second of a Date (nanosecond is set to 0).
15 | - `static startOfMinute(date: Date) -> Date`: reset to start of the minute of a Date (second and nanosecond are set to 0s)
16 | - `static startOfHour(date: Date) -> Date` : reset to start of the hour of a Date (minute, second and nanosecond are set to 0s).
17 | - `static startOfDay(date: Date) -> Date` : reset to start of the day of a Date (hour, minute, second and nanosecond are set to 0s).
18 | - `static startOfDate(date: Date) -> Date` : alias of `startOfDay`
19 | - `static startOfMonth(date: Date) -> Date` : reset to start of the month of a Date (day is set to 1; hour, minute, second and nanosecond are set to 0s).
20 | - `static startOfYear(date: Date) -> Date` : reset to start of the year of a Date (month and day are set to 1s; hour, minute, second and nanosecond are set to 0s).
21 | - `static startOfWeek(date: Date) -> Date` : reset to start of the week of a Date (day is set to `first week day`; hour, minute, second and nanosecond are set to 0s).
22 | - `static addSeconds(date: Date, numSeconds: Int) -> Date`: add n seconds to a Date.
23 | - `static addMinutes(date: Date, numMinutes: Int) -> Date`: add n minutes to a Date.
24 | - `static addHours(date: Date, numHours: Int) -> Date` : add n hours to a Date.
25 | - `static addDays(date: Date, numDays: Int) -> Date` : add n days to a Date.
26 | - `static addMonths(date: Date, numMonths: Int) -> Date` : add n months to a Date.
27 | - `static addYears(date: Date, numYears: Int) -> Date` : add n years to a Date.
28 | - `static addWeeks(date: Date, numWeeks: Int) -> Date` : add n weeks to a Date (1 week = 7 days).
29 |
30 |
31 | Changes
32 | -------
33 |
34 | - Since v1.3 - 2016-11-22: add functions `addXxx(...)`.
35 | - Since v1.1.1 - 2016-09-16: input/output type changed from `NSDate` to `Date`.
36 | - Since v1.1 - 2016-08-27: added functions `startOfXxx(...)`.
37 | - Since v1.0 - 2016-08-15: first release.
38 |
--------------------------------------------------------------------------------
/README-RSAUtils.md:
--------------------------------------------------------------------------------
1 | RSAUtils
2 | ========
3 |
4 | Utility class to encrypt/decrypt data using RSA private/public key.
5 |
6 | Objective-C version can be found at [https://github.com/ideawu/Objective-C-RSA](https://github.com/ideawu/Objective-C-RSA)
7 |
8 | **Note: if you receive error status -34018 on Xcode8 / iOS 10, enable Keychain Sharing. See: http://stackoverflow.com/questions/38456471/secitemadd-always-returns-error-34018-in-xcode-8-in-ios-10-simulator/38543243#38543243**
9 |
10 |
11 | Introduction
12 | ------------
13 |
14 | This library is to solve the following problem (actually mine):
15 |
16 | - We already had a server that provides APIs for clients to call. Data has to be encrypted using RSA, and the server had already generated a pair of RSA public/private key for each client.
17 | - We had to implement an iOS client for the server's APIs. It seemed to be a simple task at first, but it soon turned out not so:
18 | 1. In Apple's Security framework all key management happens at the keychain level. It means existing key(s) must be imported to keychain in order to be usable. Using the key-pair generated by `SecKeyGeneratePair` was easy. Importing an existing key (in file, or as a string), however, turned out to be tricky.
19 | 2. There was a solution in [Objective-C](https://github.com/ideawu/Objective-C-RSA) but we needed a pure-Swift one and there was no good pure-Swift implementation at the time.
20 | 3. The amount of data that can be encrypted in one go is limitted by the key's length. If a large amount of data needs to be encrypted, it has to be processed chunk by chunk.
21 |
22 | This small library addresses all 3 problems above.
23 |
24 |
25 | Usage
26 | -----
27 |
28 | *Notes:*
29 |
30 | >>>Public key: only `X509` public key is supported. On disk, `X509` public key file begins with `-----BEGIN PUBLIC KEY-----` and ends with `-----END PUBLIC KEY-----`.
31 | >>>
32 | >>>Private key: `PKCS#1` and `PKCS#8` private keys are supported. On disk, `PKCS#8 private key` file begins with `-----BEGIN PRIVATE KEY-----` and ends with `-----END PRIVATE KEY-----`, `PKCS#1 private key` file begins with `-----BEGIN RSA PRIVATE KEY-----` and ends with `-----END RSA PRIVATE KEY-----`.
33 |
34 | *Obtain `PKCS#8 private key` and `X509 public key`:*
35 |
36 | Usually you have the `PKCS#1 private key` in a `.pem` file (this file starts with `-----BEGIN RSA PRIVATE KEY-----` and ends with `-----END RSA PRIVATE KEY-----`). To convert to `PKCS#8` format, use the following command:
37 |
38 | >>> `openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in privkey.pem -out privkey.pkcs8.pem`
39 |
40 | You can extract `X509 public key` from a PEM private key by using the following command:
41 |
42 | >>> `openssl rsa -pubout -in privkey.pem -out pubkey.x509.pem`
43 |
44 | or
45 |
46 | >>> `openssl rsa -pubout -in privkey.pkcs8.pem -out pubkey.x509.pem`
47 |
48 | Example of a `PKCS#1 private key`:
49 |
50 | ```
51 | -----BEGIN RSA PRIVATE KEY-----
52 | MIICXQIBAAKBgQDAF4nCtUoppj4taUW7T4+aGhf45lGR8mAvVnKHufTzPaNinYzl
53 | mkLF7I7dk0Tgc3KoACJ6I2NC//mhEpNErX0MAqVlCMbAgWvUNSFvHByaqC/5o5hu
54 | k3CwvERFho2IHmSMMTasMG6PoOhto3KPF9AxszkRCsY3xnS5JURE/4h4DQIDAQAB
55 | AoGAcwOk8Hgr15Q6VmZZ4jVY/iPxho4g+QnunWldWfb1u06ErV84JaGfqXJieDjd
56 | XKbDV+P8wLS/kjZB+TduBgj6fVTqTdL26sBsAhTiYTBK4UoXwRMg14dbXJrsnH5n
57 | KUU9GFslFQefRaWn4mjvaMFkUqRsrLkM2nmgBXDnmJLdmwECQQDd40S4EuwoLFYW
58 | xDiWpdCn2nA6n7SBp2xa26Az7bZog6KcDxGgNPY1Z1+PGnnOw6Lcgmx2LOZS4g4S
59 | WwzjnuQ1AkEA3Z+cFFaGll9di5J2BWlQNKzv5UeSMZqvspxsd2VDJXmWs2PvqEHd
60 | BzU4WW8WsHWyoycOY0R710t8JcWCrMGPeQJBAIl6rTNYFhZ2EgkdHurIZlX6FBte
61 | pJrIv2w0NDi4ipKLLQ+Ajq0y43IHUL/76Yjg0mHKNaWrADJOeeWJoJzP8BkCQQCo
62 | zHSYmP6RcblSIQ97N6c7N6zQOR8EYQkJRVLn7VyjImTB5ZAX23J5lvOASrhBVqPk
63 | 2E6BFRwt8vRv4GuTISohAkAImxaeC1+jiRGN+EtsNDSmgNjILg5Tyteor6t3guTR
64 | uMv2Y8tJiCh7UTguUnWiDo/RLrDjuvoE4lLpbyAvariY
65 | -----END RSA PRIVATE KEY-----
66 | ```
67 |
68 | It's `PKCS#8 private key` format:
69 |
70 | ```
71 | -----BEGIN PRIVATE KEY-----
72 | MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMAXicK1SimmPi1p
73 | RbtPj5oaF/jmUZHyYC9Wcoe59PM9o2KdjOWaQsXsjt2TROBzcqgAInojY0L/+aES
74 | k0StfQwCpWUIxsCBa9Q1IW8cHJqoL/mjmG6TcLC8REWGjYgeZIwxNqwwbo+g6G2j
75 | co8X0DGzOREKxjfGdLklRET/iHgNAgMBAAECgYBzA6TweCvXlDpWZlniNVj+I/GG
76 | jiD5Ce6daV1Z9vW7ToStXzgloZ+pcmJ4ON1cpsNX4/zAtL+SNkH5N24GCPp9VOpN
77 | 0vbqwGwCFOJhMErhShfBEyDXh1tcmuycfmcpRT0YWyUVB59FpafiaO9owWRSpGys
78 | uQzaeaAFcOeYkt2bAQJBAN3jRLgS7CgsVhbEOJal0KfacDqftIGnbFrboDPttmiD
79 | opwPEaA09jVnX48aec7DotyCbHYs5lLiDhJbDOOe5DUCQQDdn5wUVoaWX12LknYF
80 | aVA0rO/lR5Ixmq+ynGx3ZUMleZazY++oQd0HNThZbxawdbKjJw5jRHvXS3wlxYKs
81 | wY95AkEAiXqtM1gWFnYSCR0e6shmVfoUG16kmsi/bDQ0OLiKkostD4COrTLjcgdQ
82 | v/vpiODSYco1pasAMk555YmgnM/wGQJBAKjMdJiY/pFxuVIhD3s3pzs3rNA5HwRh
83 | CQlFUuftXKMiZMHlkBfbcnmW84BKuEFWo+TYToEVHC3y9G/ga5MhKiECQAibFp4L
84 | X6OJEY34S2w0NKaA2MguDlPK16ivq3eC5NG4y/Zjy0mIKHtROC5SdaIOj9EusOO6
85 | +gTiUulvIC9quJg=
86 | -----END PRIVATE KEY-----
87 | ```
88 |
89 | And it's `X509 public key`:
90 |
91 | ```
92 | -----BEGIN PUBLIC KEY-----
93 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAF4nCtUoppj4taUW7T4+aGhf4
94 | 5lGR8mAvVnKHufTzPaNinYzlmkLF7I7dk0Tgc3KoACJ6I2NC//mhEpNErX0MAqVl
95 | CMbAgWvUNSFvHByaqC/5o5huk3CwvERFho2IHmSMMTasMG6PoOhto3KPF9AxszkR
96 | CsY3xnS5JURE/4h4DQIDAQAB
97 | -----END PUBLIC KEY-----
98 | ```
99 |
100 | *Functions:*
101 |
102 | Key management functions:
103 |
104 | - `static func getRSAKeyFromKeychain(_ tagName: String) -> SecKey?`: Gets an existing RSA key specified by a tag from keychain.
105 | - `static func deleteRSAKeyFromKeychain(_ tagName: String)`: Deletes an existing RSA key specified by a tag from keychain.
106 | - `static func addRSAPrivateKey(_ privkeyBase64: String, tagName: String) throws -> SecKey?`: Adds a RSA private key to keychain and returns its SecKey reference.
107 | - The private key is in base-64 PKCS#1 or PKCS#8 format.
108 | - Throws `RSAUtilsError` if the input key is not a valid PKCS#8 private key.
109 | - `-----BEGIN PRIVATE KEY-----`, `-----BEGIN RSA PRIVATE KEY-----`, `-----END PRIVATE KEY-----` and `-----END RSA PRIVATE KEY-----` can be omitted.
110 | - `static func addRSAPublicKey(_ pubkeyBase64: String, tagName: String) throws -> SecKey?`: Adds a RSA public key to keychain and returns its SecKey reference.
111 | - The public key is in base-64 X509 format.
112 | - Throws `RSAUtilsError` if the input key is not a valid X509 public key.
113 | - `-----BEGIN PUBLIC KEY-----` and `-----END PUBLIC KEY-----` can be omitted.
114 |
115 | Encryption/Decryption functions:
116 |
117 | - `static func encryptWithRSAKey(data: Data, tagName: String) -> Data?`: Encrypts data using a RSA key from keychain specified by `tagName`.
118 | - `static func encryptWithRSAKey(str: String, tagName: String) -> Data?`: Encrypts a string using a RSA key from keychain specified by `tagName`.
119 | - `static func decryptWithRSAKey(encryptedData: Data, tagName: String) -> Data?`: Decrypts an encrypted data using a RSA key from keychain specified by `tagName`.
120 | - `static func encryptWithRSAPublicKey(data: Data, pubkeyBase64: String) throws -> Data?`: Encrypts data using RSA public key.
121 | - The public key is in base-64 X509 format.
122 | - Throws `RSAUtilsError` if the input key is not a valid X509 public key.
123 | - `-----BEGIN PUBLIC KEY-----` and `-----END PUBLIC KEY-----` can be omitted.
124 | - `static func encryptWithRSAPublicKey(str: String, pubkeyBase64: String) throws -> Data?`: Encrypts a string using RSA public key.
125 | - `static func encryptWithRSAPublicKey(data: Data, pubkeyBase64: String, tagName: String) throws -> Data?`: Encrypts data using RSA public key. Also, the public key will be stored in keychain specified by `tagName`.
126 | - `static func encryptWithRSAPublicKey(str: String, pubkeyBase64: String, tagName: String) throws -> Data?`: Encrypts a string using RSA public key. Also, the public key will be stored in keychain specified by `tagName`.
127 | - `static func decryptWithRSAPrivateKey(encryptedData: Data, privkeyBase64: String) throws -> Data?`: Decrypts an encrypted data using a RSA private key.
128 | - `static func decryptWithRSAPrivateKey(encryptedData: Data, privkeyBase64: String, tagName: String) throws -> Data?`: Decrypts an encrypted data using a RSA private key. Also, the private key will be stored in keychain specified by `tagName`.
129 |
130 |
131 | Encrypt/Decrypt Large Amount of Data
132 | ------------------------------------
133 |
134 | The amount of data that can be encrypted in one go is `key's size - 11`. For example, if the key's size is 256 bytes (2048 bits), maximum amount of data that can be encrypted is 245 bytes.
135 |
136 | In order to encrypt a large amount of data, `encryptXXX()` function splits the large data into chunks of size `key's size - 11`, encrypts each chunk and concatenates encrypted chunks into the final result.
137 |
138 | Decryption performs similarly: encrypted data is splitted into chunks of size `key's size`, each chunk is then decrypted and concatenated into the final result.
139 |
140 |
141 | Changes
142 | -------
143 |
144 | - Since v1.2.1 - 2016-09-18: add support for RSA PKCS#1 private key.
145 | - Since v1.2.0 - 2016-09-17: migrated from [https://github.com/btnguyen2k/swift-rsautils](https://github.com/btnguyen2k/swift-rsautils).
146 |
--------------------------------------------------------------------------------
/README-SnowflakeIdGenerator.md:
--------------------------------------------------------------------------------
1 | SnowflakeIdGenerator
2 | ====================
3 |
4 | Swift implementation of Twitter's Snowflake algorithm.
5 |
6 |
7 | Usage
8 | -----
9 |
10 | ```swift
11 | let idGen = SwiftIdGenerator()
12 |
13 | // or
14 | // let nodeId: UInt64 = calcDeviceIdAsInt()
15 | // let idGen = SwiftIdGenerator(nodeId)
16 |
17 | let id64 = idGen.generateId64() // generate a 64-bit id
18 | let id64Hex = idGen.generateid64Hex() // generate a 64-bit id as a hex string
19 | let id128Hex = idGen.generateid128Hex() // generate a 128-bit id as a hex string
20 |
21 | // extract the timestamp as a UNIX timestamp in millisecond from a generated id
22 | let timestamp = SwiftIdGenerator.extractTimestamp64(id64)
23 |
24 | // extract the timestamp as a Date from a generated id
25 | let date = SwiftIdGenerator.extractTimestamp128HexAsDate(id128Hex)
26 | ```
27 |
28 |
29 | Functions
30 | ---------
31 |
32 | - 64-bit ID (format Format `<41-bits: timestamp><21-bits: node id><2-bits: sequence number>`, where timestamp is in millisec, minus the epoch):
33 | - `generateId64() -> UInt64`: generate a 64-bit id.
34 | - `generateId64Hex() -> String`: generate a 64-bit id as a hex string.
35 | - `generateId64Bin() -> String`: generate a 64-bit id as a binary string.
36 | - `static extractTimestamp64(id: UInt64) -> UInt64`: extract the UNIX timestamp from a 64-bit id generated by `generateId64()`.
37 | - `static extractTimestamp64Hex(idHex: String) -> UInt64`: extract the UNIX timestamp from a 64-bit id generated by `generateId64Hex()`.
38 | - `static extractTimestamp64Bin(idBin: String) -> UInt64`: extract the UNIX timestamp from a 64-bit id generated by `generateId64Bin()`.
39 | - `static extractTimestamp64AsDate(id: UInt64) -> Date`: extract the timestamp part from a 64-bit id generated by `generateId64()` as a `Date`.
40 | - `static extractTimestamp64HexAsDate(idHex: String) -> Date`: extract the timestamp part from a 64-bit id generated by `generateId64Hex()` as a `Date`.
41 | - `static extractTimestamp64BinAsDate(idBin: String) -> Date`: extract the timestamp part from a 64-bit id generated by `generateId64Bin()` as a `Date`.
42 | - 64-bit-nil ID (format Format `<41-bits: timestamp><23-bits: node id>` (no sequence part), where timestamp is in millisec, minus the epoch):
43 | - `generateId64Nil() -> UInt64`: generate a 64-bit id.
44 | - `generateId64NilHex() -> String`: generate a 64-bit id as a hex string.
45 | - `generateId64NilBin() -> String`: generate a 64-bit id as a binary string.
46 | - `static extractTimestamp64Nil(id: UInt64) -> UInt64`: extract the UNIX timestamp from a 64-bit id generated by `generateId64Nil()`.
47 | - `static extractTimestamp64NilHex(idHex: String) -> UInt64`: extract the UNIX timestamp from a 64-bit id generated by `generateId64NilHex()`.
48 | - `static extractTimestamp64NilBin(idBin: String) -> UInt64`: extract the UNIX timestamp from a 64-bit id generated by `generateId64NilBin()`.
49 | - `static extractTimestamp64NilAsDate(id: UInt64) -> Date`: extract the timestamp part from a 64-bit id generated by `generateId64Nil()` as a `Date`.
50 | - `static extractTimestamp64NilHexAsDate(idHex: String) -> Date`: extract the timestamp part from a 64-bit id generated by `generateId64NilHex()` as a `Date`.
51 | - `static extractTimestamp64NilBinAsDate(idBin: String) -> Date`: extract the timestamp part from a 64-bit id generated by `generateId64NilBin()` as a `Date`.
52 | - 128-bit ID (format Format `<64-bits: timestamp><48-bits: node id><16-bits: sequence number>`, where timestamp is in millisec):
53 | - `generateId128() -> [UInt64]`: generate a 128-bit id as an array of two UInt64s.
54 | - `generateId128Hex() -> String`: generate a 128-bit id as a hex string.
55 | - `generateId128Bin() -> String`: generate a 128-bit id as a binary string.
56 | - `static extractTimestamp128Hex(idHex: String) -> UInt64`: extract the UNIX timestamp from a 128-bit id generated by `generateId128Hex()`.
57 | - `static extractTimestamp128Bin(idBin: String) -> UInt64`: extract the UNIX timestamp from a 128-bit id generated by `generateId128Bin()`.
58 | - `static extractTimestamp128HexAsDate(idHex: String) -> Date`: extract the timestamp part from a 128-bit id generated by `generateId128Hex()` as a `Date`.
59 | - `static extractTimestamp128BinAsDate(idBin: String) -> Date`: extract the timestamp part from a 128-bit id generated by `generateId128Bin()` as a `Date`.
60 |
61 |
62 | Changes
63 | -------
64 |
65 | - Since v1.1.1 - 2016-09-16: output type changed from `NSDate` to `Date`.
66 | - Since v1.0 - 2016-08-15: first release
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SwiftUtils
2 | ==========
3 |
4 | Utility Library in Swift.
5 |
6 | By Thanh Ba Nguyen (btnguyen2k (at) gmail.com).
7 |
8 | Project home:
9 | [https://github.com/btnguyen2k/swiftutils](https://github.com/btnguyen2k/swiftutils)
10 |
11 |
12 | ## License ##
13 |
14 | See LICENSE.txt for details. Copyright (c) 2016 Thanh Ba Nguyen.
15 |
16 | Third party libraries are distributed under their own license(s).
17 |
18 |
19 | ## Release Notes ##
20 |
21 | Latest release version: `1.3`. See [RELEASE-NOTES.md](RELEASE-NOTES.md) for details.
22 |
23 |
24 | DateTimeUtils
25 | -------------
26 |
27 | Date & time utility class. See [README-DateTimeUtils.md](README-DateTimeUtils.md).
28 |
29 |
30 | SnowflakeIdGenerator
31 | --------------------
32 |
33 | Swift implementation of Twitter's Snowflake algorithm. See [README-SnowflakeIdGenerator.md](README-SnowflakeIdGenerator.md).
34 |
35 |
36 | RSAUtils
37 | --------
38 |
39 | Utility class to encrypt/decrypt data using RSA public/private key. See [README-RSAUtils.md](README-RSAUtils.md).
40 |
41 |
42 | SyncUtils
43 | ---------
44 |
45 | * Synchronizes a code block: *
46 |
47 | ```swift
48 | let result = synchronizd(lockObj) {
49 | // .... code here ....
50 | }
51 | ```
52 |
--------------------------------------------------------------------------------
/RELEASE-NOTES.md:
--------------------------------------------------------------------------------
1 | SwiftUtils Release Notes
2 | ========================
3 |
4 | 1.3 - 2016-11-22
5 | ------------------
6 |
7 | - `DateTimeUtils`: add new utility functions
8 | - `addSeconds(_ date: Date, numSeconds: Int) -> Date`
9 | - `addMinutes(_ date: Date, numMinutes: Int) -> Date`
10 | - `addHours (_ date: Date, numHours : Int) -> Date`
11 | - `addDays (_ date: Date, numDays : Int) -> Date`
12 | - `addMonths (_ date: Date, numMonths : Int) -> Date`
13 | - `addYears (_ date: Date, numYears : Int) -> Date`
14 | - `addWeeks (_ date: Date, numWeeks : Int) -> Date`
15 |
16 |
17 | 1.2.1 - 2016-09-18
18 | ------------------
19 |
20 | - `RSAUtils`: add support for RSA PKCS#1 private key.
21 |
22 |
23 | 1.2.0 - 2016-09-17
24 | ------------------
25 |
26 | - `RSAUtils`: utility class to encrypt/decrypt data with RSA public/private key.
27 |
28 |
29 | 1.1.1 - 2016-09-16
30 | ------------------
31 |
32 | - Migrated to Swift3/XCode8.
33 | - `DateTimeUtils`: input/output type changed from `NSDate` to `Date`
34 | - Documents updated.
35 |
36 |
37 | 1.1 - 2016-08-27
38 | ----------------
39 |
40 | - `DateTimeUtils`: utility functions
41 | - `startOfSecond(_ date: NSDate) -> NSDate`
42 | - `startOfMinute(_ date: NSDate) -> NSDate`
43 | - `startOfHour (_ date: NSDate) -> NSDate`
44 | - `startOfDay (_ date: NSDate) -> NSDate`
45 | - `startOfMonth (_ date: NSDate) -> NSDate`
46 | - `startOfYear (_ date: NSDate) -> NSDate`
47 | - `startOfWeek (_ date: NSDate) -> NSDate`
48 |
49 |
50 | 1.0 - 2016-08-15
51 | ----------------
52 |
53 | First release:
54 | - `SnowflakeIdGenerator`: utility class to generate ID using Twitter's Snowflake algorithm.
55 | - `DateTimeUtils`: some useful datetime utility functions.
56 |
--------------------------------------------------------------------------------
/SwiftUtils.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 |
3 | import UIKit
4 | import SwiftUtils
5 | @testable import SwiftUtils
6 |
7 | /*--- START DateTimeUtils ----*/
8 | //let df = DateFormatter()
9 | //df.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSZ"
10 | //
11 | //let d1 = Date()
12 | //print("Original date :", d1)
13 | //print("Date format :", df.string(from: d1))
14 | //print("+2 weeks format:", df.string(from: DateTimeUtils.addWeeks(d1, numWeeks: 2)))
15 | /*--- END DateTimeUtils ----*/
16 |
17 | /*--- START RSAUtils ----*/
18 | let pubKey64 = "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkw7pYdYdpFJhpb0D4hWesYA7hvC7ykRGO8VlsiJYuf7BgLZeUE5OYEP+mynePuHEh6SPhzkxgHp/LFSQdFrNIJRslOrE35yAEE2+nYQzZSgDPIFC8tN9458/bFiKExFfgUn8Eu6rhXGSPJOD93dRPspVCq0ZyOKzANPJfi8kixb/kDUU2ZKr9Gc8Rgt1JWgED4eribrtU14AvUzXibF5n1TFmbQ33Ik22U3Rrm6i914cP4de5fn02WiIBA1ohXwQRemUxlb1Sj7YFKqMo8nNBioGoRMsSpbzBiYyQfN93VE/ehMUQIjAx5AIhALo5qMWuGjYYQZo5Tz5EhnqyzUvD6F0R3Ii2GcMTPIqHmmciLQNIyLdJoG9NJxbGpEzogNIoFQ4BNGYQLlZ6Z7wHTKZs5nvWCA0Pbb1t24jQ2rgw+gEQ1krcM/Q6zuL5+3pIARiSmt3wrNYrzCZ9gM/xmxE8OFizLA9KjOnEUqB6ULj0KT6MkFB8EqN4VHSnLsbALfefTw57UldYQweui0GRZwQ+dnjs7rd7DvAIifdDjY8AVX4iJ2qYMwUiHhjlKBjPW1CQpeU+Emdl1oY1/gTHOeJOoBZd8wRpLFFY7V0JR2aQhi9eVwpn5GT0w4DML+tNDbil2m8StySom760MbnHtIy6HIK7E4HF8Iq9lZZpsKUL+MCAwEAAQ=="
19 | let privKey64 = "MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCTDulh1h2kUmGlvQPiFZ6xgDuG8LvKREY7xWWyIli5/sGAtl5QTk5gQ/6bKd4+4cSHpI+HOTGAen8sVJB0Ws0glGyU6sTfnIAQTb6dhDNlKAM8gULy033jnz9sWIoTEV+BSfwS7quFcZI8k4P3d1E+ylUKrRnI4rMA08l+LySLFv+QNRTZkqv0ZzxGC3UlaAQPh6uJuu1TXgC9TNeJsXmfVMWZtDfciTbZTdGubqL3Xhw/h17l+fTZaIgEDWiFfBBF6ZTGVvVKPtgUqoyjyc0GKgahEyxKlvMGJjJB833dUT96ExRAiMDHkAiEAujmoxa4aNhhBmjlPPkSGerLNS8PoXRHciLYZwxM8ioeaZyItA0jIt0mgb00nFsakTOiA0igVDgE0ZhAuVnpnvAdMpmzme9YIDQ9tvW3biNDauDD6ARDWStwz9DrO4vn7ekgBGJKa3fCs1ivMJn2Az/GbETw4WLMsD0qM6cRSoHpQuPQpPoyQUHwSo3hUdKcuxsAt959PDntSV1hDB66LQZFnBD52eOzut3sO8AiJ90ONjwBVfiInapgzBSIeGOUoGM9bUJCl5T4SZ2XWhjX+BMc54k6gFl3zBGksUVjtXQlHZpCGL15XCmfkZPTDgMwv600NuKXabxK3JKibvrQxuce0jLocgrsTgcXwir2VlmmwpQv4wIDAQABAoICADtPiFEtSkc78qYl1asZjSeJ0HhcH2E0qB7zPlDaCW76kJCY/PDmpDWvNsDd4gT6iYkrlbe0GYL35NK8SIalGJZLRn/JMB3/wKbStE5TQ2dI/AUrHRVStzNWxv2ruAR4vUwnysLX/9WSOKi11kO4m8v3TtU/e2bKB+gNgvahjNuMKwa+YepefXvVJzoY5OZxTTH2RfkGqzE0eJfgpg2ixqq8RfX3+Y+4x6Zc/HSdqpyaGEqHDn8ykM4emr0Hq2Aq6WVJD/fumn+XWQ78ujxU55vU11efo/1xZBLW1O5Ehj78xd02VLSycw4JyWV0xvo+TKuTnbGEJEpQjSP/z/KlQJUtaY+vaicRwGV5uLVA7jxJQlFHrENQd47sRsZEnwmsBwUWfu7ZSf6YLhnV5XRM5oumnSM9wfiJR3XlOAz6wBYRjY/XbnYW/4uHFmxEXkjuWQBWyI9/OTYHYTrZDgIWnmdW4bP+9tuXzbaSkAisW850w59z3lLD5967WMuYRNgJYXrxc415dWlKU4N3n8eyhAr86dJ+yzIg/mfoAN+uSns+cOm+wnjjOiA8Ea6cusVpm9+UoXgjr6Ir4f4oDzYJYuo4snJL0Ilb/GYPXs1sjOPHizPT9GzL+6Aei1C00tY1Vn2qaoup/4mXYJ9t+ZAsWv+QGnTLrkJxQ+A1p2jnNmYBAoIBAQDuoEfVFFNbQQSAPUjLgXlI7CREIRbsKtjqOHbOZHBw5sbf0O1tFk6+dI/SNRQQE1g8PBakIaE02hQwkS0TJ1W6V4hPIXO7o1zavkk05WqlfPY2IdlyDETB5hANsTycCA4WIYROZ9Z+aQI2xKlA8VoQoTOpdnmENOwzhhIU09hA4/hNMtKucMCnOnqB8Ab7TLBhM5Mlis79TCnqWmLI7f5svyM/I1ZFIs2FYqn1IoWA4Ldg1LIzxeBw55Gza37J0/gSmAOARCOfCgxESklOC2EVje6ZlOVvYBkBLLzi6I9bgf1lGa1fOia6qFBr7Y3kFDo84ghVN9w6iy15KAS46Oh3AoIBAQCdw+kOjMCXf77k7P/UTyes2NXc5hrF+FSHxmliXtyLZvPSlOirPYenZwkuDf9ZbpCLy1Wt66eOgU1EkFyDm4Oz1o6r1mjhc0Pua84xKgK67O9UPfxlKm2GptwYzYApLqYm5+sWanRrFGOvQkFIQdrV4IwAkl+CNyZdmzInqxtPXuTT7HXoYNKa2ZPcVXn09Ao8q6OZYEb3UcqebsKGT3f6Jwft59HBVhz7MrYm0qxp45/RtF9zDjR5lAtI3UDmTJ/CZetTNqymUkGiFb6RUVrtCSfmwzV7EdPUoU6rdJNr/Yz+DBYpjofhKa4VTx+/1Ry+g/YyvIAHQMcRMBjog3r1AoIBAQChXaX1w77ysK74gXjelXmNBJMpty6nGfqBuRkuTOF0l7rY05Ia+MDbLjurrNUaYdqDMScmas92zBpnFWVj1G+2iUbWLAr1Um2SRqd+q71Il45v4MgIxZ8heBBXEgmoghH0iUnNM47rANvMaYUDM7LyF/C1ojZeXuNznDBSAUf16bOZkMrDx9+ftn3trIEsIxoLQTM6afm7dGvL3L98nOQ2abLOGXlXW0eb8bJ44JVjJ5MaWfr/48n4z7/JkuG0XjEdccq8TFfCTqso6wtTSYKnbslw9i2I+XUvRC+fSiDXgrn2SjzhbpOOGg/4TDZti/gt/7Qe3J2CwWdIzPUUjfvVAoIBAE3GKrpVgQwumbjZbTUI4JJBQwznEwPNbD6S/ZjVEvxWAJUre5gPeTR5gvkFRYCdFP0VfGOE7NE/xQur7y+iFsctWqAzzvh1jmOXGcr63uWvsDxjYm2tANmEoLRJeojSym4bnrUqPcIcBxh7HRu1+1+8lsghYJwc+/jhbVRI8emq6jvfMjEgqjJUWKQiL0EmnXVTWA9gexupYq3ABH2Z55eWbj5GLg2Vmivr0AhLi1uYL01+Eh/yPMRCy11cVYYy9/8pp10acvp7SofRGGUjKiP4g4crbM4C9962tsWnbpWqJTuIUdSiwzGpDnzKyOgU81qoS1Kvwp3QvVIn19+oEZECggEBAMrTCHmNl9vfSz5o9vE9LVen5Kx/0PMAccDBHIHjkYg+6gUfoTW0dEPQuhtTLys3RntpjQrnuH/+RpJVwNzKKWHLmx4u8NZXR2un+0uJ18Ya++o5a04CjycO0PMVkKIcN/ujpPIo/FEMXrnN4gPMDnTMDjwCeAjjuvQUImOf6CjPIogK7S68gCplezz1OQGvlsA28y0fPUBQITQThVh+SZP4byRuS21tNk0SgJNwcf/KTvT/xfI9TmmCG9+jpDKhEymLQgEYGPRU/EhBgsSqyB4J5q8vOVhGcvRolwdWuqufl1tLTKLKHwSD7nlN8bRJiwX+JPFM0GY+as6CMlaRh98="
20 |
21 | let tagPublic = "com.github.btnguyen2k.ios.SwiftUtilsPlayground.public"
22 | let tagPrivate = "com.github.btnguyen2k.ios.SwiftUtilsPlayground.private"
23 | let keyRef = try RSAUtils.addRSAPublicKey(pubKey64, tagName: tagPublic)
24 |
25 | //let orgString = "This a string"
26 | //let encData = try! RSAUtils.encryptWithRSAPublicKey(str: orgString, pubkeyBase64: pubKey64, tagName: "com.github.btnguyen2k.ios.SwiftUtilsPlayground.public")
27 |
28 | // let eData = Data(base64Encoded: eData64, options: [])
29 | //
30 | //// let data = "I'm a string"
31 | ////
32 | //// let eData = try! RSAUtils.encryptWithRSAPublicKey(str: data, pubkeyBase64: pubKey64, tagName: "com.github.btnguyen2k.ios.SwiftUtilsPlayground.public")
33 | // let dData = try! RSAUtils.decryptWithRSAPrivateKey(encryptedData: eData!, privkeyBase64: privKey64, tagName: "com.github.btnguyen2k.ios.SwiftUtilsPlayground.private")
34 | // let dDataStr = String(data: dData!, encoding: .utf8)
35 | //
36 | // //NSLog("Original data : \(data)")
37 | // NSLog("Encrypted data: \(eData!)")
38 | // NSLog("Decrypted data: \(dData!)")
39 | // NSLog("Decrypted data: \(dDataStr!)")
40 |
41 |
42 | /*--- END RSAUtils ----*/
43 |
44 | //var privKeyStr = "-----BEGIN RSA PRIVATE KEY-----\n" +
45 | //"MIIEpAIBAAKCAQEAtVDMVHzXEweGJH600LQsa8cpd9lrtwEbU6mOJ6/paKB4GaDpVQLbe1B6vRtE\n" +
46 | //"QHYVjvJRUZvMpP/C5iNWrI8MCsxp5QfySPU1O+lVOK9/sCZZNZvbjLnNF1MRbgW0cNZ6B69hAPk7\n" +
47 | //"61+yKYvhLOqQvFbiKj4zsXNdALO9yt5ogP1is0KclBEZjHgv3hWeTQqe2W9qk7W4AyNXML9So5Za\n" +
48 | //"Ftm4xj/y6N8uRDpJ9WSRJkbZY1C/GaGLOSyCAEdrpqJiv5fHN5JgjCTGA1TZC+XrIVsLh4SRSgaZ\n" +
49 | //"owgDADvDL7bke0NcZo7YDNn8xEarKURSBqdEkvt6TNipYd3vpSXCLwIDAQABAoIBAQCo/YeT708d\n" +
50 | //"w1N5TEcGhgco5NV3iMNmK7fmgA+oWcJ366hEar9Opn0eL+JVwsYYNnbdZmf4YfeYiXCDwsgTyLd2\n" +
51 | //"kRLZjlB5Q2G8VztAvAGMkhFvb7ZvMPVAGgISw2psCNpvszv8Bk+cjvKk2mSi0nTXtTrSGGthpp41\n" +
52 | //"+1CXdjd8kBqnZGy1PjQDclT5RQOQPppIV1QE5f4HJYsEjipjvEAf9KGcukL5+U2X1EYJue2cfTGH\n" +
53 | //"VLVj9D5COsEsVX3yoJd7v6+Vv2QFJOkbQ0AR49vxezhoIVyB4Wona8jhMu5nbToB+0xwjtBPnOmr\n" +
54 | //"KIa9sX8+lamBh8Ig8HCP0GZrpEPhAoGBAOnzQaK/LCCC9RmKS6W16EkTc5lb5AlQqiT2HUsUDFzn\n" +
55 | //"A3ylTvUtlorsWMQTm1sidvgpFYRzNwGChK/mby55GmD7Ki4E35saXM9O8Sc5K+xHOm/Qm7ug0h/V\n" +
56 | //"Z3vtPaygvct3Me9QTr3EzV+//LzkP0fydRuCb1yVlgdsh9ixJ393AoGBAMZnk2MN8I9hnZtNxUYH\n" +
57 | //"K2kmJUYj2kl6RdI4HQJDefrJ6Ax+1sop1+y9wipy/7kaGvEtMjLPWQ1FDlqd63mSKDK8CoHUtppX\n" +
58 | //"UI79w/d5aAkKBtEYS2opqYHwVoDeBJpMtII9mzGbi//mpK1peB3wKFUw8422eb5oWR2gvUXzp7EJ\n" +
59 | //"AoGAe683jCs10RprKk2LINjDLZ8zBmkmbiZPTaswYXj/8D3SAhwDWmv3owrBAyi6gbv+2cQetB+V\n" +
60 | //"fR7bt4NEVHWCFLO04Q+CiCWv24oIxIvdRNWDd4n6S4AClwHj7AjJCnMQ5Hjn1XXHq1xYl76Qh+zD\n" +
61 | //"5+2vLBQRfAk5gENIrq86CpECgYAM1UA8RPeNXqQCGpqq9+W/7F621bTV0dnYShbeQZ0PMqYWwNGF\n" +
62 | //"zAUSZAe+FSgjQeQCO4kmrjC1YgUWqWZEu8Zr2avYT/jrrMAiL5nQVQL8qrBcL/UYzc8P/4qo1TN5\n" +
63 | //"A4MddXynTcdVawa1zL+fOBxzvjHStb3UKg2xk+cKMkNjeQKBgQDJ8bo0s/eMDw5F9oCIEOopCi2N\n" +
64 | //"qxs6mkglF78bwO39uN92+6yK+Ue92y+DkgeYhr43hLbL7vOZmijPire4hqHsl7wTf/vzwQ0DrXkd\n" +
65 | //"75r6xcez9oCLa61clSGP2DWSa9b+Bee0ybwWovPDeZAhnyG9rilN93DPFD374l/WREv5gA==\n" +
66 | //"-----END RSA PRIVATE KEY-----"
67 | //privKeyStr.characters.count
68 | //
69 | //let pattern = "(-----BEGIN.*?-----)|(-----END.*?-----)|\\s+"
70 | //
71 | //let regExp = try! NSRegularExpression(pattern: pattern, options: [])
72 | //
73 | //privKeyStr = regExp.stringByReplacingMatches(in: privKeyStr, options: [], range: NSRange(location: 0, length: privKeyStr.characters.count), withTemplate: "")
74 | //privKeyStr.characters.count
75 |
76 | //df.string(from: DateTimeUtils.startOfSecond(d1))
77 |
78 | //df.string(from: DateTimeUtils.addSeconds(d1, numSeconds: 5))
79 | //df.string(from: DateTimeUtils.addMinutes(d1, numMinutes: 1))
80 | //df.string(from: DateTimeUtils.addHours(d1, numHours: 1))
81 |
82 | //
83 | //let d1 = Date()
84 | //df.string(from: d1)
85 | //df.string(from: DateTimeUtils.startOfSecond(d1))
86 | //df.string(from: DateTimeUtils.startOfMinute(d1))
87 | //df.string(from: DateTimeUtils.startOfHour(d1))
88 | //df.string(from: DateTimeUtils.startOfDay(d1))
89 | //df.string(from: DateTimeUtils.startOfMonth(d1))
90 | //df.string(from: DateTimeUtils.startOfYear(d1))
91 | //df.string(from: DateTimeUtils.startOfWeek(d1))
92 | //
93 | //let d2 = NSDate()
94 | //df.string(from: d2 as Date)
95 | //
96 | //let idGen = SnowflakeIdGenerator(103)
97 | ////let idGen2 = SnowflakeIdGenerator(1981)
98 | //let id64 = idGen.generateId64()
99 | //SnowflakeIdGenerator.extractTimestamp64(id64)
100 |
101 | //
102 | //idGen.generateId128()
103 | //idGen.generateId128Hex()
104 | //idGen.generateId128Bin()
105 | //
106 | //idGen2.generateId64()
107 | //idGen2.generateId64Hex()
108 | //idGen2.generateId64Bin()
109 | //
110 | //let cal = NSCalendar.current
111 | //let date = NSDate()
112 | ////DateFormatter("yyyy-MM-dd HH:mm:ss").string(from: date as Date)
113 | //let df = DateFormatter()
114 | //df.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
115 | //
116 | //cal.component(Calendar.Component.weekday, from: date as Date)
117 | //cal.firstWeekday
118 | //
119 | ////let currentDateComponents = cal.dateComponents([.year, .month, .day, .weekOfYear], from: date as Date)
120 | ////df.string(from: cal.date(from: currentDateComponents)!)
121 | //
122 | ////df.string(from: DateTimeUtils.startOfWeek(date) as Date)
123 | //
124 | //df.string(from: date as Date)
125 | ////df.string(from: cal.startOfDay(for: date as Date))
126 | ////df.string(from: DateTimeUtils.startOfSecond(date) as Date)
127 | ////df.string(from: DateTimeUtils.startOfMinute(date) as Date)
128 | ////df.string(from: DateTimeUtils.startOfHour(date) as Date)
129 | ////df.string(from: DateTimeUtils.startOfDate(date) as Date)
130 | ////df.string(from: DateTimeUtils.startOfMonth(date) as Date)
131 | ////df.string(from: DateTimeUtils.startOfYear(date) as Date)
132 | //df.string(from: DateTimeUtils.startOfWeek(date) as Date)
133 | //cal.component(Calendar.Component.weekday, from: date as Date)
134 | //var d = DateTimeUtils.startOfDate(date) as Date
135 | //cal.date(byAdding: Calendar.Component.weekday, value: -1, to: d)!
136 | //
137 | ////cal.startOfDay(for: date as Date)
138 | ////DateTimeUtils.startOfMinute(date)
139 | ////var comps = cal.dateComponents([Calendar.Component.day, Calendar.Component.month, Calendar.Component.year, Calendar.Component.weekOfYear, Calendar.Component.hour, Calendar.Component.minute, Calendar.Component.second, Calendar.Component.nanosecond], from: date as Date)
140 | ////comps.setValue(1, for: Calendar.Component.hour)
141 | ////cal.date(from: comps)
142 | //
143 | ////let components: NSDateComponents = cal?.components(NSCalendar.Unit.CalendarUnit, fromDate: date)
144 | //
145 | //date
146 | //
147 | //
148 | ///*
149 | //idGen.generateId64()
150 | //idGen.generateId64Hex()
151 | //idGen.generateId64Bin()
152 | //
153 | //idGen2.generateId64()
154 | //idGen2.generateId64Hex()
155 | //idGen2.generateId64Bin()
156 | //
157 | //idGen.getLastTimestampMillisec()
158 | //idGen2.getLastTimestampMillisec()
159 | //
160 | //idGen.generateId128()
161 | //idGen.generateId128Hex()
162 | //idGen.generateId128Bin()
163 | //
164 | //idGen2.generateId128()
165 | //idGen2.generateId128Hex()
166 | //idGen2.generateId128Bin()
167 | //*/
168 | //
169 | ////String(12345, radix: 2)
170 | ////String(83630470, radix: 2)
171 | //
172 | ////idGen.generateId64()
173 | ////idGen.generateId128()
174 | ////var id = idGen.generateId128Hex()
175 | ////let index = id.index(id.endIndex, offsetBy: -16)
176 | ////id.substring(to: index)
177 | ////SnowflakeIdGenerator.extractTimestamp128Hex(idHex: id)
178 | ////SnowflakeIdGenerator.extractTimestamp128HexAsDate(idHex: id)
179 | //
180 | ////idGen2.generateId64()
181 | ////idGen2.generateId128()
182 | ////var id2 = idGen2.generateId128Hex()
183 | ////SnowflakeIdGenerator.extractTimestamp128Hex(idHex: id2)
184 | ////SnowflakeIdGenerator.extractTimestamp128HexAsDate(idHex: id2)
185 |
--------------------------------------------------------------------------------
/SwiftUtils.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/SwiftUtils.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 45;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4C1BAB781D6210A1003B699E /* RELEASE-NOTES.md in Sources */ = {isa = PBXBuildFile; fileRef = 4C1BAB771D6210A1003B699E /* RELEASE-NOTES.md */; };
11 | 4C2AD78F1D605B2D003AF2DD /* SwiftUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C2AD7851D605B2D003AF2DD /* SwiftUtils.framework */; };
12 | 4C2AD7941D605B2D003AF2DD /* SwiftUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2AD7931D605B2D003AF2DD /* SwiftUtilsTests.swift */; };
13 | 4C2AD7A21D605C08003AF2DD /* SnowflakeIdGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2AD7A11D605C08003AF2DD /* SnowflakeIdGenerator.swift */; };
14 | 4C4AC9911D8E635F00F72BE3 /* README-RSAUtils.md in Sources */ = {isa = PBXBuildFile; fileRef = 4C4AC9901D8E635F00F72BE3 /* README-RSAUtils.md */; };
15 | 4CC4BC261D6158D0000EF79D /* DateTimeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC4BC251D6158D0000EF79D /* DateTimeUtils.swift */; };
16 | 4CC4BC281D615D5F000EF79D /* SyncUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC4BC271D615D5F000EF79D /* SyncUtils.swift */; };
17 | 4CC4BC2C1D620F32000EF79D /* LICENSE.txt in Resources */ = {isa = PBXBuildFile; fileRef = 4CC4BC291D620F32000EF79D /* LICENSE.txt */; };
18 | 4CC4BC2D1D620F32000EF79D /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 4CC4BC2A1D620F32000EF79D /* README.md */; };
19 | 4CC503291D8BB30300967A15 /* README-DateTimeUtils.md in Sources */ = {isa = PBXBuildFile; fileRef = 4CC503281D8BB30300967A15 /* README-DateTimeUtils.md */; };
20 | 4CC5032B1D8BB55300967A15 /* README-SnowflakeIdGenerator.md in Sources */ = {isa = PBXBuildFile; fileRef = 4CC5032A1D8BB55300967A15 /* README-SnowflakeIdGenerator.md */; };
21 | 4CC5032D1D8BBBDD00967A15 /* RSAUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC5032C1D8BBBDD00967A15 /* RSAUtils.swift */; };
22 | /* End PBXBuildFile section */
23 |
24 | /* Begin PBXContainerItemProxy section */
25 | 4C2AD7901D605B2D003AF2DD /* PBXContainerItemProxy */ = {
26 | isa = PBXContainerItemProxy;
27 | containerPortal = 4C2AD77C1D605B2D003AF2DD /* Project object */;
28 | proxyType = 1;
29 | remoteGlobalIDString = 4C2AD7841D605B2D003AF2DD;
30 | remoteInfo = SwiftUtils;
31 | };
32 | /* End PBXContainerItemProxy section */
33 |
34 | /* Begin PBXFileReference section */
35 | 4C1BAB771D6210A1003B699E /* RELEASE-NOTES.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "RELEASE-NOTES.md"; sourceTree = ""; };
36 | 4C2AD7851D605B2D003AF2DD /* SwiftUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; };
37 | 4C2AD7891D605B2D003AF2DD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
38 | 4C2AD78E1D605B2D003AF2DD /* SwiftUtilsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftUtilsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
39 | 4C2AD7931D605B2D003AF2DD /* SwiftUtilsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUtilsTests.swift; sourceTree = ""; };
40 | 4C2AD7951D605B2D003AF2DD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
41 | 4C2AD7A11D605C08003AF2DD /* SnowflakeIdGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnowflakeIdGenerator.swift; sourceTree = ""; };
42 | 4C4AC9901D8E635F00F72BE3 /* README-RSAUtils.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "README-RSAUtils.md"; sourceTree = ""; };
43 | 4CC4BC251D6158D0000EF79D /* DateTimeUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateTimeUtils.swift; sourceTree = ""; };
44 | 4CC4BC271D615D5F000EF79D /* SyncUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncUtils.swift; sourceTree = ""; };
45 | 4CC4BC291D620F32000EF79D /* LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; };
46 | 4CC4BC2A1D620F32000EF79D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
47 | 4CC503281D8BB30300967A15 /* README-DateTimeUtils.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "README-DateTimeUtils.md"; sourceTree = ""; };
48 | 4CC5032A1D8BB55300967A15 /* README-SnowflakeIdGenerator.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "README-SnowflakeIdGenerator.md"; sourceTree = ""; };
49 | 4CC5032C1D8BBBDD00967A15 /* RSAUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RSAUtils.swift; sourceTree = ""; };
50 | /* End PBXFileReference section */
51 |
52 | /* Begin PBXFrameworksBuildPhase section */
53 | 4C2AD7811D605B2D003AF2DD /* Frameworks */ = {
54 | isa = PBXFrameworksBuildPhase;
55 | buildActionMask = 2147483647;
56 | files = (
57 | );
58 | runOnlyForDeploymentPostprocessing = 0;
59 | };
60 | 4C2AD78B1D605B2D003AF2DD /* Frameworks */ = {
61 | isa = PBXFrameworksBuildPhase;
62 | buildActionMask = 2147483647;
63 | files = (
64 | 4C2AD78F1D605B2D003AF2DD /* SwiftUtils.framework in Frameworks */,
65 | );
66 | runOnlyForDeploymentPostprocessing = 0;
67 | };
68 | /* End PBXFrameworksBuildPhase section */
69 |
70 | /* Begin PBXGroup section */
71 | 4C2AD77B1D605B2D003AF2DD = {
72 | isa = PBXGroup;
73 | children = (
74 | 4C4AC9901D8E635F00F72BE3 /* README-RSAUtils.md */,
75 | 4CC5032A1D8BB55300967A15 /* README-SnowflakeIdGenerator.md */,
76 | 4CC503281D8BB30300967A15 /* README-DateTimeUtils.md */,
77 | 4C1BAB771D6210A1003B699E /* RELEASE-NOTES.md */,
78 | 4CC4BC291D620F32000EF79D /* LICENSE.txt */,
79 | 4CC4BC2A1D620F32000EF79D /* README.md */,
80 | 4C2AD7871D605B2D003AF2DD /* SwiftUtils */,
81 | 4C2AD7921D605B2D003AF2DD /* SwiftUtilsTests */,
82 | 4C2AD7861D605B2D003AF2DD /* Products */,
83 | );
84 | sourceTree = "";
85 | };
86 | 4C2AD7861D605B2D003AF2DD /* Products */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 4C2AD7851D605B2D003AF2DD /* SwiftUtils.framework */,
90 | 4C2AD78E1D605B2D003AF2DD /* SwiftUtilsTests.xctest */,
91 | );
92 | name = Products;
93 | sourceTree = "";
94 | };
95 | 4C2AD7871D605B2D003AF2DD /* SwiftUtils */ = {
96 | isa = PBXGroup;
97 | children = (
98 | 4C2AD7891D605B2D003AF2DD /* Info.plist */,
99 | 4C2AD7A11D605C08003AF2DD /* SnowflakeIdGenerator.swift */,
100 | 4CC4BC251D6158D0000EF79D /* DateTimeUtils.swift */,
101 | 4CC4BC271D615D5F000EF79D /* SyncUtils.swift */,
102 | 4CC5032C1D8BBBDD00967A15 /* RSAUtils.swift */,
103 | );
104 | path = SwiftUtils;
105 | sourceTree = "";
106 | };
107 | 4C2AD7921D605B2D003AF2DD /* SwiftUtilsTests */ = {
108 | isa = PBXGroup;
109 | children = (
110 | 4C2AD7931D605B2D003AF2DD /* SwiftUtilsTests.swift */,
111 | 4C2AD7951D605B2D003AF2DD /* Info.plist */,
112 | );
113 | path = SwiftUtilsTests;
114 | sourceTree = "";
115 | };
116 | /* End PBXGroup section */
117 |
118 | /* Begin PBXHeadersBuildPhase section */
119 | 4C2AD7821D605B2D003AF2DD /* Headers */ = {
120 | isa = PBXHeadersBuildPhase;
121 | buildActionMask = 2147483647;
122 | files = (
123 | );
124 | runOnlyForDeploymentPostprocessing = 0;
125 | };
126 | /* End PBXHeadersBuildPhase section */
127 |
128 | /* Begin PBXNativeTarget section */
129 | 4C2AD7841D605B2D003AF2DD /* SwiftUtils */ = {
130 | isa = PBXNativeTarget;
131 | buildConfigurationList = 4C2AD7991D605B2D003AF2DD /* Build configuration list for PBXNativeTarget "SwiftUtils" */;
132 | buildPhases = (
133 | 4C2AD7801D605B2D003AF2DD /* Sources */,
134 | 4C2AD7811D605B2D003AF2DD /* Frameworks */,
135 | 4C2AD7821D605B2D003AF2DD /* Headers */,
136 | 4C2AD7831D605B2D003AF2DD /* Resources */,
137 | );
138 | buildRules = (
139 | );
140 | dependencies = (
141 | );
142 | name = SwiftUtils;
143 | productName = SwiftUtils;
144 | productReference = 4C2AD7851D605B2D003AF2DD /* SwiftUtils.framework */;
145 | productType = "com.apple.product-type.framework";
146 | };
147 | 4C2AD78D1D605B2D003AF2DD /* SwiftUtilsTests */ = {
148 | isa = PBXNativeTarget;
149 | buildConfigurationList = 4C2AD79C1D605B2D003AF2DD /* Build configuration list for PBXNativeTarget "SwiftUtilsTests" */;
150 | buildPhases = (
151 | 4C2AD78A1D605B2D003AF2DD /* Sources */,
152 | 4C2AD78B1D605B2D003AF2DD /* Frameworks */,
153 | 4C2AD78C1D605B2D003AF2DD /* Resources */,
154 | );
155 | buildRules = (
156 | );
157 | dependencies = (
158 | 4C2AD7911D605B2D003AF2DD /* PBXTargetDependency */,
159 | );
160 | name = SwiftUtilsTests;
161 | productName = SwiftUtilsTests;
162 | productReference = 4C2AD78E1D605B2D003AF2DD /* SwiftUtilsTests.xctest */;
163 | productType = "com.apple.product-type.bundle.unit-test";
164 | };
165 | /* End PBXNativeTarget section */
166 |
167 | /* Begin PBXProject section */
168 | 4C2AD77C1D605B2D003AF2DD /* Project object */ = {
169 | isa = PBXProject;
170 | attributes = {
171 | LastSwiftUpdateCheck = 0800;
172 | LastUpgradeCheck = 1150;
173 | ORGANIZATIONNAME = "Thanh Nguyen";
174 | TargetAttributes = {
175 | 4C2AD7841D605B2D003AF2DD = {
176 | CreatedOnToolsVersion = 8.0;
177 | LastSwiftMigration = 1140;
178 | };
179 | 4C2AD78D1D605B2D003AF2DD = {
180 | CreatedOnToolsVersion = 8.0;
181 | ProvisioningStyle = Automatic;
182 | };
183 | };
184 | };
185 | buildConfigurationList = 4C2AD77F1D605B2D003AF2DD /* Build configuration list for PBXProject "SwiftUtils" */;
186 | compatibilityVersion = "Xcode 3.1";
187 | developmentRegion = en;
188 | hasScannedForEncodings = 0;
189 | knownRegions = (
190 | en,
191 | Base,
192 | );
193 | mainGroup = 4C2AD77B1D605B2D003AF2DD;
194 | productRefGroup = 4C2AD7861D605B2D003AF2DD /* Products */;
195 | projectDirPath = "";
196 | projectRoot = "";
197 | targets = (
198 | 4C2AD7841D605B2D003AF2DD /* SwiftUtils */,
199 | 4C2AD78D1D605B2D003AF2DD /* SwiftUtilsTests */,
200 | );
201 | };
202 | /* End PBXProject section */
203 |
204 | /* Begin PBXResourcesBuildPhase section */
205 | 4C2AD7831D605B2D003AF2DD /* Resources */ = {
206 | isa = PBXResourcesBuildPhase;
207 | buildActionMask = 2147483647;
208 | files = (
209 | 4CC4BC2C1D620F32000EF79D /* LICENSE.txt in Resources */,
210 | );
211 | runOnlyForDeploymentPostprocessing = 0;
212 | };
213 | 4C2AD78C1D605B2D003AF2DD /* Resources */ = {
214 | isa = PBXResourcesBuildPhase;
215 | buildActionMask = 2147483647;
216 | files = (
217 | );
218 | runOnlyForDeploymentPostprocessing = 0;
219 | };
220 | /* End PBXResourcesBuildPhase section */
221 |
222 | /* Begin PBXSourcesBuildPhase section */
223 | 4C2AD7801D605B2D003AF2DD /* Sources */ = {
224 | isa = PBXSourcesBuildPhase;
225 | buildActionMask = 2147483647;
226 | files = (
227 | 4C4AC9911D8E635F00F72BE3 /* README-RSAUtils.md in Sources */,
228 | 4CC5032D1D8BBBDD00967A15 /* RSAUtils.swift in Sources */,
229 | 4C1BAB781D6210A1003B699E /* RELEASE-NOTES.md in Sources */,
230 | 4CC4BC261D6158D0000EF79D /* DateTimeUtils.swift in Sources */,
231 | 4C2AD7A21D605C08003AF2DD /* SnowflakeIdGenerator.swift in Sources */,
232 | 4CC5032B1D8BB55300967A15 /* README-SnowflakeIdGenerator.md in Sources */,
233 | 4CC503291D8BB30300967A15 /* README-DateTimeUtils.md in Sources */,
234 | 4CC4BC2D1D620F32000EF79D /* README.md in Sources */,
235 | 4CC4BC281D615D5F000EF79D /* SyncUtils.swift in Sources */,
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | };
239 | 4C2AD78A1D605B2D003AF2DD /* Sources */ = {
240 | isa = PBXSourcesBuildPhase;
241 | buildActionMask = 2147483647;
242 | files = (
243 | 4C2AD7941D605B2D003AF2DD /* SwiftUtilsTests.swift in Sources */,
244 | );
245 | runOnlyForDeploymentPostprocessing = 0;
246 | };
247 | /* End PBXSourcesBuildPhase section */
248 |
249 | /* Begin PBXTargetDependency section */
250 | 4C2AD7911D605B2D003AF2DD /* PBXTargetDependency */ = {
251 | isa = PBXTargetDependency;
252 | target = 4C2AD7841D605B2D003AF2DD /* SwiftUtils */;
253 | targetProxy = 4C2AD7901D605B2D003AF2DD /* PBXContainerItemProxy */;
254 | };
255 | /* End PBXTargetDependency section */
256 |
257 | /* Begin XCBuildConfiguration section */
258 | 4C2AD7971D605B2D003AF2DD /* Debug */ = {
259 | isa = XCBuildConfiguration;
260 | buildSettings = {
261 | ALWAYS_SEARCH_USER_PATHS = NO;
262 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
263 | CLANG_ANALYZER_NONNULL = YES;
264 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
265 | CLANG_CXX_LIBRARY = "libc++";
266 | CLANG_ENABLE_MODULES = YES;
267 | CLANG_ENABLE_OBJC_ARC = YES;
268 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
269 | CLANG_WARN_BOOL_CONVERSION = YES;
270 | CLANG_WARN_COMMA = YES;
271 | CLANG_WARN_CONSTANT_CONVERSION = YES;
272 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
275 | CLANG_WARN_EMPTY_BODY = YES;
276 | CLANG_WARN_ENUM_CONVERSION = YES;
277 | CLANG_WARN_INFINITE_RECURSION = YES;
278 | CLANG_WARN_INT_CONVERSION = YES;
279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
280 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
281 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
283 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
284 | CLANG_WARN_STRICT_PROTOTYPES = YES;
285 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
286 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
287 | CLANG_WARN_UNREACHABLE_CODE = YES;
288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
289 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
290 | COPY_PHASE_STRIP = NO;
291 | CURRENT_PROJECT_VERSION = 1;
292 | DEBUG_INFORMATION_FORMAT = dwarf;
293 | ENABLE_STRICT_OBJC_MSGSEND = YES;
294 | ENABLE_TESTABILITY = YES;
295 | GCC_C_LANGUAGE_STANDARD = gnu99;
296 | GCC_DYNAMIC_NO_PIC = NO;
297 | GCC_NO_COMMON_BLOCKS = YES;
298 | GCC_OPTIMIZATION_LEVEL = 0;
299 | GCC_PREPROCESSOR_DEFINITIONS = (
300 | "DEBUG=1",
301 | "$(inherited)",
302 | );
303 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
304 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
305 | GCC_WARN_UNDECLARED_SELECTOR = YES;
306 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
307 | GCC_WARN_UNUSED_FUNCTION = YES;
308 | GCC_WARN_UNUSED_VARIABLE = YES;
309 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
310 | MTL_ENABLE_DEBUG_INFO = YES;
311 | ONLY_ACTIVE_ARCH = YES;
312 | SDKROOT = iphoneos;
313 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
314 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
315 | SWIFT_VERSION = 4.0;
316 | TARGETED_DEVICE_FAMILY = "1,2";
317 | VERSIONING_SYSTEM = "apple-generic";
318 | VERSION_INFO_PREFIX = "";
319 | };
320 | name = Debug;
321 | };
322 | 4C2AD7981D605B2D003AF2DD /* Release */ = {
323 | isa = XCBuildConfiguration;
324 | buildSettings = {
325 | ALWAYS_SEARCH_USER_PATHS = NO;
326 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
327 | CLANG_ANALYZER_NONNULL = YES;
328 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
329 | CLANG_CXX_LIBRARY = "libc++";
330 | CLANG_ENABLE_MODULES = YES;
331 | CLANG_ENABLE_OBJC_ARC = YES;
332 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
333 | CLANG_WARN_BOOL_CONVERSION = YES;
334 | CLANG_WARN_COMMA = YES;
335 | CLANG_WARN_CONSTANT_CONVERSION = YES;
336 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
337 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
338 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
339 | CLANG_WARN_EMPTY_BODY = YES;
340 | CLANG_WARN_ENUM_CONVERSION = YES;
341 | CLANG_WARN_INFINITE_RECURSION = YES;
342 | CLANG_WARN_INT_CONVERSION = YES;
343 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
344 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
345 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
346 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
347 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
348 | CLANG_WARN_STRICT_PROTOTYPES = YES;
349 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
350 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
351 | CLANG_WARN_UNREACHABLE_CODE = YES;
352 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
353 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
354 | COPY_PHASE_STRIP = NO;
355 | CURRENT_PROJECT_VERSION = 1;
356 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
357 | ENABLE_NS_ASSERTIONS = NO;
358 | ENABLE_STRICT_OBJC_MSGSEND = YES;
359 | GCC_C_LANGUAGE_STANDARD = gnu99;
360 | GCC_NO_COMMON_BLOCKS = YES;
361 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
362 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
363 | GCC_WARN_UNDECLARED_SELECTOR = YES;
364 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
365 | GCC_WARN_UNUSED_FUNCTION = YES;
366 | GCC_WARN_UNUSED_VARIABLE = YES;
367 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
368 | MTL_ENABLE_DEBUG_INFO = NO;
369 | ONLY_ACTIVE_ARCH = YES;
370 | SDKROOT = iphoneos;
371 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
372 | SWIFT_VERSION = 4.0;
373 | TARGETED_DEVICE_FAMILY = "1,2";
374 | VALIDATE_PRODUCT = YES;
375 | VERSIONING_SYSTEM = "apple-generic";
376 | VERSION_INFO_PREFIX = "";
377 | };
378 | name = Release;
379 | };
380 | 4C2AD79A1D605B2D003AF2DD /* Debug */ = {
381 | isa = XCBuildConfiguration;
382 | buildSettings = {
383 | CLANG_ENABLE_MODULES = YES;
384 | CODE_SIGN_IDENTITY = "";
385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
386 | CODE_SIGN_STYLE = Automatic;
387 | DEFINES_MODULE = YES;
388 | DEVELOPMENT_TEAM = 7T2WS9KVUZ;
389 | DYLIB_COMPATIBILITY_VERSION = 1;
390 | DYLIB_CURRENT_VERSION = 1;
391 | DYLIB_INSTALL_NAME_BASE = "@rpath";
392 | INFOPLIST_FILE = SwiftUtils/Info.plist;
393 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
394 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
395 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
396 | PRODUCT_BUNDLE_IDENTIFIER = com.github.btnguyen2k.ios.SwiftUtils;
397 | PRODUCT_NAME = "$(TARGET_NAME)";
398 | PROVISIONING_PROFILE_SPECIFIER = "";
399 | SKIP_INSTALL = YES;
400 | SUPPORTS_MACCATALYST = NO;
401 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
402 | SWIFT_VERSION = 4.0;
403 | };
404 | name = Debug;
405 | };
406 | 4C2AD79B1D605B2D003AF2DD /* Release */ = {
407 | isa = XCBuildConfiguration;
408 | buildSettings = {
409 | CLANG_ENABLE_MODULES = YES;
410 | CODE_SIGN_IDENTITY = "";
411 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
412 | CODE_SIGN_STYLE = Automatic;
413 | DEFINES_MODULE = YES;
414 | DEVELOPMENT_TEAM = 7T2WS9KVUZ;
415 | DYLIB_COMPATIBILITY_VERSION = 1;
416 | DYLIB_CURRENT_VERSION = 1;
417 | DYLIB_INSTALL_NAME_BASE = "@rpath";
418 | INFOPLIST_FILE = SwiftUtils/Info.plist;
419 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
420 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
421 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
422 | PRODUCT_BUNDLE_IDENTIFIER = com.github.btnguyen2k.ios.SwiftUtils;
423 | PRODUCT_NAME = "$(TARGET_NAME)";
424 | PROVISIONING_PROFILE_SPECIFIER = "";
425 | SKIP_INSTALL = YES;
426 | SUPPORTS_MACCATALYST = NO;
427 | SWIFT_VERSION = 4.0;
428 | };
429 | name = Release;
430 | };
431 | 4C2AD79D1D605B2D003AF2DD /* Debug */ = {
432 | isa = XCBuildConfiguration;
433 | buildSettings = {
434 | DEVELOPMENT_TEAM = 7T2WS9KVUZ;
435 | INFOPLIST_FILE = SwiftUtilsTests/Info.plist;
436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
437 | PRODUCT_BUNDLE_IDENTIFIER = com.github.btnguyen2k.ios.SwiftUtilsTests;
438 | PRODUCT_NAME = "$(TARGET_NAME)";
439 | SWIFT_VERSION = 4.0;
440 | };
441 | name = Debug;
442 | };
443 | 4C2AD79E1D605B2D003AF2DD /* Release */ = {
444 | isa = XCBuildConfiguration;
445 | buildSettings = {
446 | DEVELOPMENT_TEAM = 7T2WS9KVUZ;
447 | INFOPLIST_FILE = SwiftUtilsTests/Info.plist;
448 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
449 | PRODUCT_BUNDLE_IDENTIFIER = com.github.btnguyen2k.ios.SwiftUtilsTests;
450 | PRODUCT_NAME = "$(TARGET_NAME)";
451 | SWIFT_VERSION = 4.0;
452 | };
453 | name = Release;
454 | };
455 | /* End XCBuildConfiguration section */
456 |
457 | /* Begin XCConfigurationList section */
458 | 4C2AD77F1D605B2D003AF2DD /* Build configuration list for PBXProject "SwiftUtils" */ = {
459 | isa = XCConfigurationList;
460 | buildConfigurations = (
461 | 4C2AD7971D605B2D003AF2DD /* Debug */,
462 | 4C2AD7981D605B2D003AF2DD /* Release */,
463 | );
464 | defaultConfigurationIsVisible = 0;
465 | defaultConfigurationName = Debug;
466 | };
467 | 4C2AD7991D605B2D003AF2DD /* Build configuration list for PBXNativeTarget "SwiftUtils" */ = {
468 | isa = XCConfigurationList;
469 | buildConfigurations = (
470 | 4C2AD79A1D605B2D003AF2DD /* Debug */,
471 | 4C2AD79B1D605B2D003AF2DD /* Release */,
472 | );
473 | defaultConfigurationIsVisible = 0;
474 | defaultConfigurationName = Debug;
475 | };
476 | 4C2AD79C1D605B2D003AF2DD /* Build configuration list for PBXNativeTarget "SwiftUtilsTests" */ = {
477 | isa = XCConfigurationList;
478 | buildConfigurations = (
479 | 4C2AD79D1D605B2D003AF2DD /* Debug */,
480 | 4C2AD79E1D605B2D003AF2DD /* Release */,
481 | );
482 | defaultConfigurationIsVisible = 0;
483 | defaultConfigurationName = Debug;
484 | };
485 | /* End XCConfigurationList section */
486 | };
487 | rootObject = 4C2AD77C1D605B2D003AF2DD /* Project object */;
488 | }
489 |
--------------------------------------------------------------------------------
/SwiftUtils.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftUtils.xcodeproj/xcuserdata/btnguyen.xcuserdatad/xcschemes/SwiftUtils.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/SwiftUtils.xcodeproj/xcuserdata/btnguyen.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SwiftUtils.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 4C2AD7841D605B2D003AF2DD
16 |
17 | primary
18 |
19 |
20 | 4C2AD78D1D605B2D003AF2DD
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/SwiftUtils.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/SwiftUtils/DateTimeUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateTimeUtils.swift
3 | // SwiftUtils
4 | //
5 | // Created by Thanh Nguyen on 8/15/16.
6 | // Copyright © 2016 Thanh Nguyen. All rights reserved.
7 | //----------------------------------------------------------------------
8 | // Useful DateTime utilities.
9 | //----------------------------------------------------------------------
10 |
11 | import Foundation
12 |
13 | public class DateTimeUtils {
14 | /**
15 | * Gets the current UNIX timestamp in milliseconds as a UInt64.
16 | *
17 | * - Returns: the current UNIX timestamp in milliseconds
18 | */
19 | @available(iOS, introduced: 1.0)
20 | public static func currentUnixTimestampMillisec() -> UInt64 {
21 | return UInt64(NSDate().timeIntervalSince1970 * 1000)
22 | }
23 |
24 | /**
25 | * Gets the current UNIX timestamp in seconds as a UInt64.
26 | *
27 | * - Returns: the current UNIX timestamp in seconds
28 | */
29 | @available(iOS, introduced: 1.0)
30 | public static func currentUnixTimestampSecond() -> UInt64 {
31 | return UInt64(NSDate().timeIntervalSince1970)
32 | }
33 |
34 | /**
35 | * Waits until clock moves to the next millisecond.
36 | *
37 | * - Parameter currentMillisec: the current timestamp in milliseconds
38 | *
39 | * - Returns: the "next" millisecond
40 | */
41 | @available(iOS, introduced: 1.0)
42 | @discardableResult public static func waitTillNextMillisec(_ currentMillisec: UInt64) -> UInt64 {
43 | var nextMillisec = currentUnixTimestampMillisec()
44 | while ( nextMillisec <= currentMillisec ) {
45 | nextMillisec = currentUnixTimestampMillisec()
46 | }
47 | return nextMillisec
48 | }
49 |
50 | /**
51 | * Waits until clock moves to the next second.
52 | *
53 | * - Parameter currentMillisec: the current timestamp in seconds
54 | *
55 | * - Returns: the "next" second
56 | */
57 | @available(iOS, introduced: 1.0)
58 | @discardableResult public static func waitTillNextSecond(_ currentSecond: UInt64) -> UInt64 {
59 | var nextSecond = currentUnixTimestampSecond()
60 | while ( nextSecond <= currentSecond ) {
61 | nextSecond = currentUnixTimestampSecond()
62 | }
63 | return nextSecond
64 | }
65 |
66 | @available(iOS, introduced: 1.1)
67 | static private let FULL_DATE_COMPONENTS: Set = [Calendar.Component.era,
68 | Calendar.Component.year,
69 | Calendar.Component.month,
70 | Calendar.Component.day,
71 | Calendar.Component.hour,
72 | Calendar.Component.minute,
73 | Calendar.Component.second,
74 | Calendar.Component.nanosecond,
75 | Calendar.Component.weekday,
76 | Calendar.Component.weekdayOrdinal,
77 | Calendar.Component.weekOfMonth,
78 | Calendar.Component.weekOfYear,
79 | Calendar.Component.quarter,
80 | Calendar.Component.yearForWeekOfYear,
81 | Calendar.Component.calendar,
82 | Calendar.Component.timeZone]
83 |
84 | /**
85 | * Resets to start of the second of (nanosecond is set to 0) a Date object.
86 | *
87 | * - Parameter date: the input Date object
88 | *
89 | * - Returns: start of the second of the input Date object
90 | */
91 | @available(iOS, introduced: 1.1)
92 | public static func startOfSecond(_ date: Date) -> Date {
93 | var comps = Calendar.current.dateComponents(FULL_DATE_COMPONENTS, from: date)
94 | comps.setValue(0, for: Calendar.Component.nanosecond)
95 | return Calendar.current.date(from: comps)!
96 | }
97 |
98 | /**
99 | * Adds n seconds to a Date object.
100 | *
101 | * - Parameter date: the input Date object
102 | * - Parameter numSeconds: the number of seconds to add
103 | *
104 | * - Returns: the result Date object
105 | */
106 | @available(iOS, introduced: 1.3)
107 | public static func addSeconds(_ date: Date, numSeconds: Int) -> Date {
108 | var comps = Calendar.current.dateComponents(FULL_DATE_COMPONENTS, from: date)
109 | comps.second! += numSeconds
110 | return Calendar.current.date(from: comps)!
111 | }
112 |
113 | /**
114 | * Resets to start of the minute (second and nanosecond are set to 0s) of a Date object.
115 | *
116 | * - Parameter date: the input Date object
117 | *
118 | * - Returns: start of the minute of the input Date object
119 | */
120 | @available(iOS, introduced: 1.1)
121 | public static func startOfMinute(_ date: Date) -> Date {
122 | let d = startOfSecond(date)
123 | var comps = Calendar.current.dateComponents(FULL_DATE_COMPONENTS, from: d)
124 | comps.setValue(0, for: Calendar.Component.second)
125 | return Calendar.current.date(from: comps)!
126 | }
127 |
128 | /**
129 | * Adds n minutes to a Date object.
130 | *
131 | * - Parameter date: the input Date object
132 | * - Parameter numMinutes: the number of minutes to add
133 | *
134 | * - Returns: the result Date object
135 | */
136 | @available(iOS, introduced: 1.3)
137 | public static func addMinutes(_ date: Date, numMinutes: Int) -> Date {
138 | var comps = Calendar.current.dateComponents(FULL_DATE_COMPONENTS, from: date)
139 | comps.minute! += numMinutes
140 | return Calendar.current.date(from: comps)!
141 | }
142 |
143 | /**
144 | * Resets to start of the hour (minute, second and nanosecond are set to 0s) of a Date object.
145 | *
146 | * - Parameter date: the input Date object
147 | *
148 | * - Returns: start of the hour of the input Date object
149 | */
150 | @available(iOS, introduced: 1.1)
151 | public static func startOfHour(_ date: Date) -> Date {
152 | let d = startOfMinute(date)
153 | var comps = Calendar.current.dateComponents(FULL_DATE_COMPONENTS, from: d)
154 | comps.setValue(0, for: Calendar.Component.minute)
155 | return Calendar.current.date(from: comps)!
156 | }
157 |
158 | /**
159 | * Adds n hours to a Date object.
160 | *
161 | * - Parameter date: the input Date object
162 | * - Parameter numHours: the number of hours to add
163 | *
164 | * - Returns: the result Date object
165 | */
166 | @available(iOS, introduced: 1.3)
167 | public static func addHours(_ date: Date, numHours: Int) -> Date {
168 | var comps = Calendar.current.dateComponents(FULL_DATE_COMPONENTS, from: date)
169 | comps.hour! += numHours
170 | return Calendar.current.date(from: comps)!
171 | }
172 |
173 | /**
174 | * Resets to start of the day (hour, minute, second and nanosecond are set to 0s) of a Date object.
175 | *
176 | * - Parameter date: the input Date object
177 | *
178 | * - Returns: start of the day of the input Date object
179 | */
180 | @available(iOS, introduced: 1.1)
181 | public static func startOfDay(_ date: Date) -> Date {
182 | let d = startOfHour(date)
183 | var comps = NSCalendar.current.dateComponents(FULL_DATE_COMPONENTS, from: d)
184 | comps.setValue(0, for: Calendar.Component.hour)
185 | return NSCalendar.current.date(from: comps)!
186 | }
187 |
188 | /**
189 | * Alias of startOfDay()
190 | */
191 | @available(iOS, introduced: 1.1)
192 | public static func startOfDate(_ date: Date) -> Date {
193 | return startOfDay(date)
194 | }
195 |
196 | /**
197 | * Adds n days to a Date object.
198 | *
199 | * - Parameter date: the input Date object
200 | * - Parameter numDays: the number of days to add
201 | *
202 | * - Returns: the result Date object
203 | */
204 | @available(iOS, introduced: 1.3)
205 | public static func addDays(_ date: Date, numDays: Int) -> Date {
206 | var comps = Calendar.current.dateComponents(FULL_DATE_COMPONENTS, from: date)
207 | comps.day! += numDays
208 | return Calendar.current.date(from: comps)!
209 | }
210 |
211 | /**
212 | * Resets to start of the month (day is set to 1; hour, minute, second and nanosecond are set to 0s) of a Date object.
213 | *
214 | * - Parameter date: the input Date object
215 | *
216 | * - Returns: start of the month of the input Date object
217 | */
218 | @available(iOS, introduced: 1.1)
219 | public static func startOfMonth(_ date: Date) -> Date {
220 | let d = startOfDay(date)
221 | var comps = NSCalendar.current.dateComponents(FULL_DATE_COMPONENTS, from: d)
222 | comps.setValue(1, for: Calendar.Component.day)
223 | return NSCalendar.current.date(from: comps)!
224 | }
225 |
226 | /**
227 | * Adds n months to a Date object.
228 | *
229 | * - Parameter date: the input Date object
230 | * - Parameter numMonths: the number of months to add
231 | *
232 | * - Returns: the result Date object
233 | */
234 | @available(iOS, introduced: 1.3)
235 | public static func addMonths(_ date: Date, numMonths: Int) -> Date {
236 | var comps = Calendar.current.dateComponents(FULL_DATE_COMPONENTS, from: date)
237 | comps.month! += numMonths
238 | return Calendar.current.date(from: comps)!
239 | }
240 |
241 | /**
242 | * Resets to start of the year (month and day is set to 1s; hour, minute, second and nanosecond are set to 0s) of a Date object.
243 | *
244 | * - Parameter date: the input Date object
245 | *
246 | * - Returns: start of the year of the input Date object
247 | */
248 | @available(iOS, introduced: 1.1)
249 | public static func startOfYear(_ date: Date) -> Date {
250 | let d = startOfMonth(date)
251 | var comps = NSCalendar.current.dateComponents(FULL_DATE_COMPONENTS, from: d)
252 | comps.setValue(1, for: Calendar.Component.month)
253 | return NSCalendar.current.date(from: comps)!
254 | }
255 |
256 | /**
257 | * Adds n years to a Date object.
258 | *
259 | * - Parameter date: the input Date object
260 | * - Parameter numYears: the number of years to add
261 | *
262 | * - Returns: the result Date object
263 | */
264 | @available(iOS, introduced: 1.3)
265 | public static func addYears(_ date: Date, numYears: Int) -> Date {
266 | var comps = Calendar.current.dateComponents(FULL_DATE_COMPONENTS, from: date)
267 | //comps.year is wrong!
268 | comps.yearForWeekOfYear! += numYears
269 | return Calendar.current.date(from: comps)!
270 | }
271 |
272 | /**
273 | * Resets to start of the week (day is set to 'first week day'; hour, minute, second and nanosecond are set to 0s) of a Date object.
274 | *
275 | * - Parameter date: the input Date object
276 | *
277 | * - Returns: start of the week of the input Date object
278 | */
279 | @available(iOS, introduced: 1.1)
280 | public static func startOfWeek(_ date: Date) -> Date {
281 | let CAL = NSCalendar.current
282 | var result = startOfDate(date)
283 | var weekday = CAL.component(Calendar.Component.weekday, from: result)
284 | while ( weekday != CAL.firstWeekday ) {
285 | result = CAL.date(byAdding: Calendar.Component.weekday, value: -1, to: result)!
286 | weekday = CAL.component(Calendar.Component.weekday, from: result)
287 | }
288 | return result
289 | }
290 |
291 | /**
292 | * Adds n weeks to a Date object.
293 | *
294 | * - Parameter date: the input Date object
295 | * - Parameter numWeeks: the number of weeks to add (1 week = 7 days)
296 | *
297 | * - Returns: the result Date object
298 | */
299 | @available(iOS, introduced: 1.3)
300 | public static func addWeeks(_ date: Date, numWeeks: Int) -> Date {
301 | return addDays(date, numDays: numWeeks*7)
302 | }
303 | }
304 |
--------------------------------------------------------------------------------
/SwiftUtils/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.2.1
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SwiftUtils/RSAUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RsaUtils.swift
3 | // SwiftUtils
4 | //
5 | // Created by Thanh Nguyen on 9/16/16.
6 | // Copyright © 2016 Thanh Nguyen. All rights reserved.
7 | //----------------------------------------------------------------------
8 | // RSA utilities.
9 | // Credits:
10 | // - https://github.com/ideawu/Objective-C-RSA
11 | // - http://netsplit.com/swift-storing-key-pairs-in-the-keyring
12 | // - http://netsplit.com/swift-generating-keys-and-encrypting-and-decrypting-text
13 | // - http://hg.mozilla.org/services/fx-home/file/tip/Sources/NetworkAndStorage/CryptoUtils.m#l1036
14 | //----------------------------------------------------------------------
15 |
16 | import Foundation
17 | import Security
18 |
19 | public class RSAUtils {
20 |
21 | private static let PADDING_FOR_DECRYPT = SecPadding()
22 |
23 | @available(iOS, introduced: 1.2.0)
24 | public class RSAUtilsError: NSError {
25 | init(_ message: String) {
26 | super.init(domain: "com.github.btnguyen2k.SwiftUtils.RSAUtils", code: 500, userInfo: [
27 | NSLocalizedDescriptionKey: message
28 | ])
29 | }
30 |
31 | @available(*, unavailable)
32 | required public init?(coder aDecoder: NSCoder) {
33 | fatalError("init(coder:) has not been implemented")
34 | }
35 | }
36 |
37 | // Base64 encode a block of data
38 | @available(iOS, introduced: 1.2.0)
39 | private static func base64Encode(_ data: Data) -> String {
40 | return data.base64EncodedString(options: [])
41 | }
42 |
43 | // Base64 decode a base64-ed string
44 | @available(iOS, introduced: 1.2.0)
45 | private static func base64Decode(_ strBase64: String) -> Data {
46 | let data = Data(base64Encoded: strBase64, options: [])
47 | return data!
48 | }
49 |
50 | /**
51 | * Deletes an existing RSA key specified by a tag from keychain.
52 | *
53 | * - Parameter tagName: tag name to query for RSA key from keychain
54 | */
55 | @available(iOS, introduced: 1.2.0)
56 | public static func deleteRSAKeyFromKeychain(_ tagName: String) {
57 | let queryFilter: [String: AnyObject] = [
58 | String(kSecClass) : kSecClassKey,
59 | String(kSecAttrKeyType) : kSecAttrKeyTypeRSA,
60 | String(kSecAttrApplicationTag): tagName as AnyObject
61 | ]
62 | SecItemDelete(queryFilter as CFDictionary)
63 | }
64 |
65 | /**
66 | * Gets an existing RSA key specified by a tag from keychain.
67 | *
68 | * - Parameter tagName: tag name to query for RSA key from keychain
69 | *
70 | * - Returns: SecKey reference to the RSA key
71 | */
72 | @available(iOS, introduced: 1.2.0)
73 | public static func getRSAKeyFromKeychain(_ tagName: String) -> SecKey? {
74 | let queryFilter: [String: AnyObject] = [
75 | String(kSecClass) : kSecClassKey,
76 | String(kSecAttrKeyType) : kSecAttrKeyTypeRSA,
77 | String(kSecAttrApplicationTag): tagName as AnyObject,
78 | //String(kSecAttrAccessible) : kSecAttrAccessibleWhenUnlocked,
79 | String(kSecReturnRef) : true as AnyObject
80 | ]
81 |
82 | var keyPtr: AnyObject?
83 | let result = SecItemCopyMatching(queryFilter as CFDictionary, &keyPtr)
84 | if ( result != noErr || keyPtr == nil ) {
85 | return nil
86 | }
87 | return keyPtr as! SecKey?
88 | }
89 |
90 | /**
91 | * Adds a RSA private key (PKCS#1 or PKCS#8) to keychain and returns its SecKey reference.
92 | *
93 | * On disk, a PEM RSA PKCS#8 private key file starts with string "-----BEGIN PRIVATE KEY-----", and ends with string "-----END PRIVATE KEY-----"; PKCS#1 private key file starts with string "-----BEGIN RSA PRIVATE KEY-----", and ends with string "-----END RSA PRIVATE KEY-----".
94 | *
95 | * - Parameter privkeyBase64: RSA private key (PKCS#1 or PKCS#8) in base64
96 | * - Parameter tagName: tag name to store RSA key to keychain
97 | *
98 | * - Throws: `RSAUtilsError` if the input key is not a valid PKCS#8 private key
99 | *
100 | * - Returns: SecKey reference to the RSA private key.
101 | */
102 | @available(iOS, introduced: 1.2.0)
103 | @discardableResult public static func addRSAPrivateKey(_ privkeyBase64: String, tagName: String) throws -> SecKey? {
104 | let fullRange = NSRange(location: 0, length: privkeyBase64.lengthOfBytes(using: .utf8))
105 | let regExp = try! NSRegularExpression(pattern: "(-----BEGIN.*?-----)|(-----END.*?-----)|\\s+", options: [])
106 | let myPrivkeyBase64 = regExp.stringByReplacingMatches(in: privkeyBase64, options: [], range: fullRange, withTemplate: "")
107 | return try addRSAPrivateKey(base64Decode(myPrivkeyBase64), tagName: tagName)
108 | }
109 |
110 | /**
111 | * Adds a RSA private key to keychain and returns its SecKey reference.
112 | *
113 | * - Parameter privkey: RSA private key (PKCS#1 or PKCS#8)
114 | * - Parameter tagName: tag name to store RSA key to keychain
115 | *
116 | * - Throws: `RSAUtilsError` if the input key is not a valid PKCS#8 private key
117 | *
118 | * - Returns: SecKey reference to the RSA private key.
119 | */
120 | @available(iOS, introduced: 1.2.0)
121 | @discardableResult private static func addRSAPrivateKey(_ privkey: Data, tagName: String) throws -> SecKey? {
122 | // Delete any old lingering key with the same tag
123 | deleteRSAKeyFromKeychain(tagName)
124 |
125 | let privkeyData = try stripPrivateKeyHeader(privkey)
126 | if ( privkeyData == nil ) {
127 | return nil
128 | }
129 |
130 | // Add persistent version of the key to system keychain
131 | let queryFilter: [String : Any] = [
132 | (kSecClass as String) : kSecClassKey,
133 | (kSecAttrKeyType as String) : kSecAttrKeyTypeRSA,
134 | (kSecAttrApplicationTag as String) : tagName,
135 | //(kSecAttrAccessible as String) : kSecAttrAccessibleWhenUnlocked,
136 | (kSecValueData as String) : privkeyData!,
137 | (kSecAttrKeyClass as String) : kSecAttrKeyClassPrivate,
138 | (kSecReturnPersistentRef as String): true
139 | ] as [String : Any]
140 | let result = SecItemAdd(queryFilter as CFDictionary, nil)
141 | if ((result != noErr) && (result != errSecDuplicateItem)) {
142 | NSLog("Cannot add key to keychain, status \(result).")
143 | return nil
144 | }
145 |
146 | return getRSAKeyFromKeychain(tagName)
147 | }
148 |
149 | /**
150 | * Verifies that the supplied key is in fact a PEM RSA private key, and strips its header.
151 | *
152 | * If the supplied key is PKCS#8, its ASN.1 header should be stripped. Otherwise (PKCS#1), the whole key data is left intact.
153 | *
154 | * On disk, a PEM RSA PKCS#8 private key file starts with string "-----BEGIN PRIVATE KEY-----", and ends with string "-----END PRIVATE KEY-----"; PKCS#1 private key file starts with string "-----BEGIN RSA PRIVATE KEY-----", and ends with string "-----END RSA PRIVATE KEY-----".
155 | *
156 | * - Parameter privkey: RSA private key (PKCS#1 or PKCS#8)
157 | *
158 | * - Throws: `RSAUtilsError` if the input key is not a valid RSA PKCS#8 private key
159 | *
160 | * - Returns: the RSA private key with header stripped.
161 | */
162 | @available(iOS, introduced: 1.2.0)
163 | private static func stripPrivateKeyHeader(_ privkey: Data) throws -> Data? {
164 | if ( privkey.count == 0 ) {
165 | return nil
166 | }
167 |
168 | var keyAsArray = [UInt8](repeating: 0, count: privkey.count / MemoryLayout.size)
169 | (privkey as NSData).getBytes(&keyAsArray, length: privkey.count)
170 |
171 | //PKCS#8: magic byte at offset 22, check if it's actually ASN.1
172 | var idx = 22
173 | if ( keyAsArray[idx] != 0x04 ) {
174 | return privkey
175 | }
176 | idx += 1
177 |
178 | //now we need to find out how long the key is, so we can extract the correct hunk
179 | //of bytes from the buffer.
180 | var len = Int(keyAsArray[idx])
181 | idx += 1
182 | let det = len & 0x80 //check if the high bit set
183 | if (det == 0) {
184 | //no? then the length of the key is a number that fits in one byte, (< 128)
185 | len = len & 0x7f
186 | } else {
187 | //otherwise, the length of the key is a number that doesn't fit in one byte (> 127)
188 | var byteCount = Int(len & 0x7f)
189 | if (byteCount + idx > privkey.count) {
190 | return nil
191 | }
192 | //so we need to snip off byteCount bytes from the front, and reverse their order
193 | var accum: UInt = 0
194 | var idx2 = idx
195 | idx += byteCount
196 | while (byteCount > 0) {
197 | //after each byte, we shove it over, accumulating the value into accum
198 | accum = (accum << 8) + UInt(keyAsArray[idx2])
199 | idx2 += 1
200 | byteCount -= 1
201 | }
202 | // now we have read all the bytes of the key length, and converted them to a number,
203 | // which is the number of bytes in the actual key. we use this below to extract the
204 | // key bytes and operate on them
205 | len = Int(accum)
206 | }
207 | return privkey.subdata(in: idx.. SecKey? {
223 | let fullRange = NSRange(location: 0, length: pubkeyBase64.lengthOfBytes(using: .utf8))
224 | let regExp = try! NSRegularExpression(pattern: "(-----BEGIN.*?-----)|(-----END.*?-----)|\\s+", options: [])
225 | let myPubkeyBase64 = regExp.stringByReplacingMatches(in: pubkeyBase64, options: [], range: fullRange, withTemplate: "")
226 | return try addRSAPublicKey(base64Decode(myPubkeyBase64), tagName: tagName)
227 | }
228 |
229 | /**
230 | * Adds a RSA pubic key to keychain and returns its SecKey reference.
231 | *
232 | * - Parameter pubkey: X509 public key
233 | * - Parameter tagName: tag name to store RSA key to keychain
234 | *
235 | * - Throws: `RSAUtilsError` if the input key is not a valid X509 public key
236 | *
237 | * - Returns: SecKey reference to the RSA public key.
238 | */
239 | @available(iOS, introduced: 1.2.0)
240 | private static func addRSAPublicKey(_ pubkey: Data, tagName: String) throws -> SecKey? {
241 | // Delete any old lingering key with the same tag
242 | deleteRSAKeyFromKeychain(tagName)
243 |
244 | let pubkeyData = try stripPublicKeyHeader(pubkey)
245 | if ( pubkeyData == nil ) {
246 | return nil
247 | }
248 |
249 | // Add persistent version of the key to system keychain
250 | //var prt1: Unmanaged?
251 | let queryFilter: [String : Any] = [
252 | (kSecClass as String) : kSecClassKey,
253 | (kSecAttrKeyType as String) : kSecAttrKeyTypeRSA,
254 | (kSecAttrApplicationTag as String) : tagName,
255 | (kSecValueData as String) : pubkeyData!,
256 | (kSecAttrKeyClass as String) : kSecAttrKeyClassPublic,
257 | (kSecReturnPersistentRef as String): true
258 | ] as [String : Any]
259 | let result = SecItemAdd(queryFilter as CFDictionary, nil)
260 | if ((result != noErr) && (result != errSecDuplicateItem)) {
261 | return nil
262 | }
263 |
264 | return getRSAKeyFromKeychain(tagName)
265 | }
266 |
267 | /**
268 | * Verifies that the supplied key is in fact a X509 public key, and strips its header.
269 | *
270 | * On disk, a X509 public key file starts with string "-----BEGIN PUBLIC KEY-----", and ends with string "-----END PUBLIC KEY-----"
271 | *
272 | * - Parameter pubkey: X509 public key
273 | *
274 | * - Throws: `RSAUtilsError` if the input key is not a valid X509 public key
275 | *
276 | * - Returns: the RSA public key with header stripped.
277 | */
278 | @available(iOS, introduced: 1.2.0)
279 | private static func stripPublicKeyHeader(_ pubkey: Data) throws -> Data? {
280 | if ( pubkey.count == 0 ) {
281 | return nil
282 | }
283 |
284 | var keyAsArray = [UInt8](repeating: 0, count: pubkey.count / MemoryLayout.size)
285 | (pubkey as NSData).getBytes(&keyAsArray, length: pubkey.count)
286 |
287 | var idx = 0
288 | if (keyAsArray[idx] != 0x30) {
289 | throw RSAUtilsError("Provided key doesn't have a valid ASN.1 structure (first byte should be 0x30).")
290 | //return nil
291 | }
292 | idx += 1
293 |
294 | if (keyAsArray[idx] > 0x80) {
295 | idx += Int(keyAsArray[idx]) - 0x80 + 1
296 | } else {
297 | idx += 1
298 | }
299 |
300 | /*
301 | * If current byte is 0x02, it means the key doesn't have a X509 header (it contains only modulo & public exponent). In this case, we can just return the provided DER data as is
302 | */
303 | if (Int(keyAsArray[idx]) == 0x02) {
304 | return pubkey
305 | }
306 |
307 | let seqiod = [UInt8](arrayLiteral: 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00)
308 | for i in idx.. 0x80) {
323 | idx += Int(keyAsArray[idx]) - 0x80 + 1;
324 | } else {
325 | idx += 1
326 | }
327 |
328 | if (keyAsArray[idx] != 0x00) {
329 | throw RSAUtilsError("Invalid byte at index \(idx) (\(keyAsArray[idx])) for public key header.")
330 | //return nil
331 | }
332 | idx += 1
333 | return pubkey.subdata(in: idx.. Data? {
348 | let blockSize = SecKeyGetBlockSize(rsaKeyRef)
349 | let dataSize = data.count / MemoryLayout.size
350 | let maxChunkSize = padding==SecPadding.OAEP ? (blockSize - 42) : (blockSize - 11)
351 |
352 | var dataAsArray = [UInt8](repeating: 0, count: dataSize)
353 | (data as NSData).getBytes(&dataAsArray, length: dataSize)
354 |
355 | var encryptedData = [UInt8](repeating: 0, count: 0)
356 | var idx = 0
357 | while (idx < dataAsArray.count ) {
358 | var idxEnd = idx + maxChunkSize
359 | if ( idxEnd > dataAsArray.count ) {
360 | idxEnd = dataAsArray.count
361 | }
362 | var chunkData = [UInt8](repeating: 0, count: maxChunkSize)
363 | for i in idx..(encryptedData), count: encryptedData.count)
381 | }
382 |
383 | /**
384 | * Decrypts data with a RSA key.
385 | *
386 | * - Parameter encryptedData: the data to be decrypted
387 | * - Parameter rsaKeyRef: the RSA key
388 | * - Parameter padding: padding used for decryption
389 | *
390 | * - Returns: the decrypted data
391 | */
392 | @available(iOS, introduced: 1.2.0)
393 | public static func decryptWithRSAKey(_ encryptedData: Data, rsaKeyRef: SecKey, padding: SecPadding) -> Data? {
394 | let blockSize = SecKeyGetBlockSize(rsaKeyRef)
395 | let dataSize = encryptedData.count / MemoryLayout.size
396 |
397 | var encryptedDataAsArray = [UInt8](repeating: 0, count: dataSize)
398 | (encryptedData as NSData).getBytes(&encryptedDataAsArray, length: dataSize)
399 |
400 | var decryptedData = [UInt8](repeating: 0, count: 0)
401 | var idx = 0
402 | while (idx < encryptedDataAsArray.count ) {
403 | var idxEnd = idx + blockSize
404 | if ( idxEnd > encryptedDataAsArray.count ) {
405 | idxEnd = encryptedDataAsArray.count
406 | }
407 | var chunkData = [UInt8](repeating: 0, count: blockSize)
408 | for i in idx..(decryptedData), count: decryptedData.count)
426 | }
427 |
428 | @available(iOS, introduced: 1.2.0)
429 | private static func removePadding(_ data: [UInt8]) -> [UInt8] {
430 | var idxFirstZero = -1
431 | var idxNextZero = data.count
432 | for i in 0.. Data? {
465 | let keyRef = getRSAKeyFromKeychain(tagName)
466 | if ( keyRef == nil ) {
467 | return nil
468 | }
469 |
470 | return encryptWithRSAKey(data, rsaKeyRef: keyRef!, padding: SecPadding.PKCS1)
471 | }
472 |
473 | /**
474 | * Encrypts a string using a RSA key from keychain specified by `tagName`.
475 | *
476 | * Note: The RSA key must be added to keychain by calling `addRSAPublicKey()` or `addRSAPrivateKey()` period to calling this function.
477 | *
478 | * - Parameter str: string to be encrypted
479 | * - Parameter tagName: tag name to query for RSA key from keychain.
480 | *
481 | * - Returns: the data in encrypted form
482 | */
483 | @available(iOS, introduced: 1.2.0)
484 | public static func encryptWithRSAKey(str: String, tagName: String) -> Data? {
485 | let keyRef = getRSAKeyFromKeychain(tagName)
486 | if ( keyRef == nil ) {
487 | return nil
488 | }
489 |
490 | return encryptWithRSAKey(str.data(using: .utf8)!, rsaKeyRef: keyRef!, padding: SecPadding.PKCS1)
491 | }
492 |
493 | /**
494 | * Decrypts an encrypted data using a RSA key from keychain specified by `tagName`.
495 | *
496 | * Note: The RSA key must be added to keychain by calling `addRSAPublicKey()` or `addRSAPrivateKey()` period to calling this function.
497 | *
498 | * - Parameter encryptedData: data to be decrypted
499 | * - Parameter tagName: tag name to query for RSA key from keychain.
500 | *
501 | * - Returns: the decrypted data
502 | */
503 | @available(iOS, introduced: 1.2.0)
504 | public static func decryptWithRSAKey(encryptedData: Data, tagName: String) -> Data? {
505 | let keyRef = getRSAKeyFromKeychain(tagName)
506 | if ( keyRef == nil ) {
507 | return nil
508 | }
509 |
510 | return decryptWithRSAKey(encryptedData, rsaKeyRef: keyRef!, padding: PADDING_FOR_DECRYPT)
511 | }
512 |
513 | /*----------------------------------------------------------------------*/
514 |
515 | /**
516 | * Encrypts data using RSA public key.
517 | *
518 | * Note: the public key will be stored in keychain with tag as `pubkeyBase64.hashValue`.
519 | *
520 | * - Parameter data: data to be encrypted
521 | * - Parameter pubkeyBase64: X509 public key in base64 (data between "-----BEGIN PUBLIC KEY-----" and "-----END PUBLIC KEY-----")
522 | *
523 | * - Throws: `RSAUtilsError` if the supplied key is not a valid X509 public key
524 | *
525 | * - Returns: the data in encrypted form
526 | */
527 | @available(iOS, introduced: 1.2.0)
528 | public static func encryptWithRSAPublicKey(data: Data, pubkeyBase64: String) throws -> Data? {
529 | let tagName = "PUBIC-" + String(pubkeyBase64.hashValue)
530 | var keyRef = getRSAKeyFromKeychain(tagName)
531 | if ( keyRef == nil ) {
532 | keyRef = try addRSAPublicKey(pubkeyBase64, tagName: tagName)
533 | }
534 | if ( keyRef == nil ) {
535 | return nil
536 | }
537 |
538 | return encryptWithRSAKey(data, rsaKeyRef: keyRef!, padding: SecPadding.PKCS1)
539 | }
540 |
541 | /**
542 | * Encrypts a string using RSA public key.
543 | *
544 | * Note: the public key will be stored in keychain with tag as `pubkeyBase64.hashValue`.
545 | *
546 | * - Parameter str: string to be encrypted
547 | * - Parameter pubkeyBase64: X509 public key in base64 (data between "-----BEGIN PUBLIC KEY-----" and "-----END PUBLIC KEY-----")
548 | *
549 | * - Throws: `RSAUtilsError` if the supplied key is not a valid X509 public key
550 | *
551 | * - Returns: the data in encrypted form
552 | */
553 | @available(iOS, introduced: 1.2.0)
554 | public static func encryptWithRSAPublicKey(str: String, pubkeyBase64: String) throws -> Data? {
555 | let tagName = "PUBIC-" + String(pubkeyBase64.hashValue)
556 | var keyRef = getRSAKeyFromKeychain(tagName)
557 | if ( keyRef == nil ) {
558 | keyRef = try addRSAPublicKey(pubkeyBase64, tagName: tagName)
559 | }
560 | if ( keyRef == nil ) {
561 | return nil
562 | }
563 |
564 | return encryptWithRSAKey(str.data(using: .utf8)!, rsaKeyRef: keyRef!, padding: SecPadding.PKCS1)
565 | }
566 |
567 | /**
568 | * Encrypts data using RSA public key.
569 | *
570 | * Note: the public key will be stored in keychain specified by tagName.
571 | *
572 | * - Parameter data: data to be encrypted
573 | * - Parameter pubkeyBase64: X509 public key in base64 (data between "-----BEGIN PUBLIC KEY-----" and "-----END PUBLIC KEY-----")
574 | * - Parameter tagName: tag name to store RSA key to keychain
575 | *
576 | * - Throws: `RSAUtilsError` if the supplied key is not a valid X509 public key
577 | *
578 | * - Returns: the data in encrypted form
579 | */
580 | @available(iOS, introduced: 1.2.0)
581 | public static func encryptWithRSAPublicKey(data: Data, pubkeyBase64: String, tagName: String) throws -> Data? {
582 | var keyRef = getRSAKeyFromKeychain(tagName)
583 | if ( keyRef == nil ) {
584 | keyRef = try addRSAPublicKey(pubkeyBase64, tagName: tagName)
585 | }
586 | if ( keyRef == nil ) {
587 | return nil
588 | }
589 |
590 | return encryptWithRSAKey(data, rsaKeyRef: keyRef!, padding: SecPadding.PKCS1)
591 | }
592 |
593 | /**
594 | * Encrypts a string using RSA public key.
595 | *
596 | * Note: the public key will be stored in keychain specified by tagName.
597 | *
598 | * - Parameter str: string to be encrypted
599 | * - Parameter pubkeyBase64: X509 public key in base64 (data between "-----BEGIN PUBLIC KEY-----" and "-----END PUBLIC KEY-----")
600 | * - Parameter tagName: tag name to store RSA key to keychain
601 | *
602 | * - Throws: `RSAUtilsError` if the supplied key is not a valid X509 public key
603 | *
604 | * - Returns: the data in encrypted form
605 | */
606 | @available(iOS, introduced: 1.2.0)
607 | public static func encryptWithRSAPublicKey(str: String, pubkeyBase64: String, tagName: String) throws -> Data? {
608 | var keyRef = getRSAKeyFromKeychain(tagName)
609 | if ( keyRef == nil ) {
610 | keyRef = try addRSAPublicKey(pubkeyBase64, tagName: tagName)
611 | }
612 | if ( keyRef == nil ) {
613 | return nil
614 | }
615 |
616 | return encryptWithRSAKey(str.data(using: .utf8)!, rsaKeyRef: keyRef!, padding: SecPadding.PKCS1)
617 | }
618 |
619 | /*----------------------------------------------------------------------*/
620 |
621 | /**
622 | * Decrypts an encrypted data using a RSA private key.
623 | *
624 | * Note: the private key will be stored in keychain with tag as `privkeyBase64.hashValue`.
625 | *
626 | * - Parameter encryptedData: data to be decrypted
627 | * - Parameter privkeyBase64: RSA PKCS#8 private key in base64 (data between "-----BEGIN PRIVATE KEY-----" and "-----END PRIVATE KEY-----")
628 | *
629 | * - Throws: `RSAUtilsError` if the supplied key is not a valid RSA PKCS#8 private key
630 | *
631 | * - Returns: the decrypted data
632 | */
633 | @available(iOS, introduced: 1.2.0)
634 | public static func decryptWithRSAPrivateKey(encryptedData: Data, privkeyBase64: String) throws -> Data? {
635 | let tagName = "PRIVATE-" + String(privkeyBase64.hashValue)
636 | var keyRef = getRSAKeyFromKeychain(tagName)
637 | if ( keyRef == nil ) {
638 | keyRef = try addRSAPrivateKey(privkeyBase64, tagName: tagName)
639 | }
640 | if ( keyRef == nil ) {
641 | return nil
642 | }
643 |
644 | return decryptWithRSAKey(encryptedData, rsaKeyRef: keyRef!, padding: PADDING_FOR_DECRYPT)
645 | }
646 |
647 | /**
648 | * Decrypts an encrypted data using a RSA private key.
649 | *
650 | * Note: the private key will be stored in keychain specified by tagName.
651 | *
652 | * - Parameter encryptedData: data to be decrypted
653 | * - Parameter privkeyBase64: RSA PKCS#8 private key in base64 (data between "-----BEGIN PRIVATE KEY-----" and "-----END PRIVATE KEY-----")
654 | * - Parameter tagName: tag name to store RSA key to keychain
655 | *
656 | * - Throws: `RSAUtilsError` if the supplied key is not a valid RSA PKCS#8 private key
657 | *
658 | * - Returns: the data in encrypted form
659 | */
660 | @available(iOS, introduced: 1.2.0)
661 | public static func decryptWithRSAPrivateKey(encryptedData: Data, privkeyBase64: String, tagName: String) throws -> Data? {
662 | var keyRef = getRSAKeyFromKeychain(tagName)
663 | if ( keyRef == nil ) {
664 | keyRef = try addRSAPrivateKey(privkeyBase64, tagName: tagName)
665 | }
666 | if ( keyRef == nil ) {
667 | return nil
668 | }
669 |
670 | return decryptWithRSAKey(encryptedData, rsaKeyRef: keyRef!, padding: SecPadding.OAEP)
671 | }
672 | }
673 |
--------------------------------------------------------------------------------
/SwiftUtils/SnowflakeIdGenerator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SnowflakeIdGenerator.swift
3 | // SwiftUtils
4 | //
5 | // Created by Thanh Nguyen on 8/14/16.
6 | // Copyright © 2016 Thanh Nguyen . All rights reserved.
7 | //----------------------------------------------------------------------
8 | // Swift implementation of Twitter's Snowflake.
9 | // * 128-bit ID:
10 | // <64-bits: timestamp><48-bits: node id><16-bits: sequence number>
11 | // (where timestamp is UNIX timestamp in milliseconds)
12 | // * 64-bit ID:
13 | // <41-bits: timestamp><21-bits: node id><2-bits: sequence number>
14 | // (where timestamp is UNIX timestamp in milliseconds, minus the epoch)
15 | // * 64-bit-nil ID:
16 | // <41-bits: timestamp><23-bits: node id>
17 | // (where timestamp is UNIX timestamp in milliseconds, minus the epoch)
18 | //----------------------------------------------------------------------
19 |
20 | import Foundation
21 | import UIKit
22 |
23 | public class SnowflakeIdGenerator {
24 | private static let MASK_TIMESTAMP_64 :UInt64 = (1 << 41) - 1 // 41 bits
25 | private static let MASK_NODE_ID_64 :UInt64 = (1 << 21) - 1 // 21 bits
26 | private static let MASK_SEQUENCE_64 :UInt64 = (1 << 2) - 1 // 2 bits
27 | private static let MAX_SEQUENCE_64 :UInt64 = (1 << 2) - 1 // 2 bits
28 | private static let SHIFT_TIMESTAMP_64 :UInt64 = 21+2
29 | private static let SHIFT_NODE_ID_64 :UInt64 = 2
30 |
31 | private static let MASK_TIMESTAMP_64NIL :UInt64 = (1 << 41) - 1 // 41 bits
32 | private static let MASK_NODE_ID_64NIL :UInt64 = (1 << 23) - 1 // 23 bits
33 | private static let SHIFT_TIMESTAMP_64NIL :UInt64 = 23
34 |
35 | private static let TIMESTAMP_EPOCH :UInt64 = 1456790400000 // 1-Mar-2016 GMT
36 |
37 | private static let MASK_NODE_ID_128 :UInt64 = 0xFFFFFFFFFFFF // 48 bits
38 | private static let MASK_SEQUENCE_128 :UInt64 = 0xFFFF // 16 bits
39 | private static let MAX_SEQUENCE_128 :UInt64 = 0xFFFF // 16 bits
40 | private static let SHIFT_TIMESTAMP_128 :UInt64 = 64
41 | private static let SHIFT_NODE_ID_128 :UInt64 = 16
42 |
43 | private var nodeId :UInt64 = 0
44 | private var template64 :UInt64 = 0
45 | private var template64Nil :UInt64 = 0
46 | private var sequenceMillisec :UInt64 = 0
47 | private var lastTimestampMillisec :UInt64 = 0
48 |
49 | init(_ nodeId: UInt64 = 0) {
50 | if ( nodeId != 0 ) {
51 | self.nodeId = nodeId
52 | } else {
53 | self.nodeId = UInt64(UIDevice.current.identifierForVendor!.hashValue)
54 | }
55 | template64 = (self.nodeId & SnowflakeIdGenerator.MASK_NODE_ID_64) << SnowflakeIdGenerator.SHIFT_NODE_ID_64
56 | template64Nil = (self.nodeId & SnowflakeIdGenerator.MASK_NODE_ID_64NIL)
57 | }
58 |
59 | public func getNodeId() -> UInt64 {
60 | return nodeId
61 | }
62 |
63 | public func getSequenceMillisec() -> UInt64 {
64 | return sequenceMillisec
65 | }
66 |
67 | public func getLastTimestampMillisec() -> UInt64 {
68 | return lastTimestampMillisec
69 | }
70 |
71 | /*----------------------------------------------------------------------*/
72 |
73 | /**
74 | * Extracts the UNIX timestamp from a 64-bit id generated by `generateId64()`.
75 | *
76 | * - Parameter id: the id generated by `generateId64()`
77 | *
78 | * - Returns: the extracted UNIX timestamp in milliseconds
79 | */
80 | @available(iOS, introduced: 1.0)
81 | public static func extractTimestamp64(_ id: UInt64) -> UInt64 {
82 | return SnowflakeIdGenerator.TIMESTAMP_EPOCH + (id >> SnowflakeIdGenerator.SHIFT_TIMESTAMP_64)
83 | }
84 |
85 | /**
86 | * Extracts the UNIX timestamp from a 64-bit id generated by `generateId64Hex()`.
87 | *
88 | * - Parameter idHex: the id generated by `generateId64Hex()`
89 | *
90 | * - Returns: the extracted UNIX timestamp in milliseconds
91 | */
92 | @available(iOS, introduced: 1.0)
93 | public static func extractTimestamp64Hex(_ idHex: String) -> UInt64 {
94 | let id = strtouq(idHex, nil, 16)
95 | return extractTimestamp64(id)
96 | }
97 |
98 | /**
99 | * Extracts the UNIX timestamp from a 64-bit id generated by `generateId64Bin()`.
100 | *
101 | * - Parameter idBin: the id generated by `generateId64Bin()`
102 | *
103 | * - Returns: the extracted UNIX timestamp in milliseconds
104 | */
105 | @available(iOS, introduced: 1.0)
106 | public static func extractTimestamp64Bin(_ idBin: String) -> UInt64 {
107 | let id = strtouq(idBin, nil, 2)
108 | return extractTimestamp64(id)
109 | }
110 |
111 | /**
112 | * Extracts the timestamp part from a 64-bit id generated by `generateId64()` as a Date.
113 | *
114 | * - Parameter id: the id generated by `generateId64()`
115 | *
116 | * - Returns: the extracted timestamp part as a Date
117 | */
118 | @available(iOS, introduced: 1.0)
119 | public static func extractTimestamp64AsDate(_ id: UInt64) -> Date {
120 | let timestamp = extractTimestamp64(id)
121 | return Date(timeIntervalSince1970: Double(timestamp) / 1000.0)
122 | }
123 |
124 | /**
125 | * Extracts the timestamp part from a 64-bit id generated by `generateId64Hex()` as a Date.
126 | *
127 | * - Parameter idHex: the id generated by `generateId64Hex()`
128 | *
129 | * - Returns: the extracted timestamp part as a Date
130 | */
131 | @available(iOS, introduced: 1.0)
132 | public static func extractTimestamp64HexAsDate(_ idHex: String) -> Date {
133 | let timestamp = extractTimestamp64Hex(idHex)
134 | return Date(timeIntervalSince1970: Double(timestamp) / 1000.0)
135 | }
136 |
137 | /**
138 | * Extracts the timestamp part from a 64-bit id generated by `generateId64Bin()` as a Date.
139 | *
140 | * - Parameter idBin: the id generated by `generateId64Bin()`
141 | *
142 | * - Returns: the extracted timestamp part as a Date
143 | */
144 | @available(iOS, introduced: 1.0)
145 | public static func extractTimestamp64BinAsDate(_ idBin: String) -> Date {
146 | let timestamp = extractTimestamp64Bin(idBin)
147 | return Date(timeIntervalSince1970: Double(timestamp) / 1000.0)
148 | }
149 |
150 | /**
151 | * Generates a 64-bit id.
152 | *
153 | * Format: `<41-bits: timestamp><21-bits: node id><2-bits: sequence number>`
154 | * Where timestamp is in milliseconds, minus the epoch.
155 | *
156 | * - Returns: 64-bit id
157 | */
158 | @available(iOS, introduced: 1.0)
159 | public func generateId64() -> UInt64 {
160 | let result = synchronizd(lock: self) {
161 | let timestamp: UInt64 = DateTimeUtils.currentUnixTimestampMillisec()
162 | var sequence: UInt64 = 0
163 | if (timestamp == self.lastTimestampMillisec) {
164 | // increase sequence
165 | sequence = self.sequenceMillisec.advanced(by: 1)
166 | if (sequence > SnowflakeIdGenerator.MAX_SEQUENCE_64) {
167 | // reset sequence
168 | self.sequenceMillisec = 0
169 | DateTimeUtils.waitTillNextMillisec(timestamp)
170 | return self.generateId64()
171 | } else {
172 | self.sequenceMillisec = sequence
173 | }
174 | } else {
175 | // reset sequence
176 | self.sequenceMillisec = sequence
177 | self.lastTimestampMillisec = timestamp
178 | }
179 | let timestampPart = (timestamp - SnowflakeIdGenerator.TIMESTAMP_EPOCH) & SnowflakeIdGenerator.MASK_TIMESTAMP_64
180 | let result = timestampPart << SnowflakeIdGenerator.SHIFT_TIMESTAMP_64 | self.template64
181 | | (sequence & SnowflakeIdGenerator.MASK_SEQUENCE_64)
182 | return result
183 |
184 | }
185 | return result as! UInt64
186 | }
187 |
188 | /**
189 | * Generates a 64-bit id as a hex string.
190 | *
191 | * Format: `<41-bits: timestamp><21-bits: node id><2-bits: sequence number>`
192 | * Where timestamp is in milliseconds, minus the epoch.
193 | *
194 | * - Returns: 64-bit id as a hex string
195 | */
196 | @available(iOS, introduced: 1.0)
197 | public func generateId64Hex() -> String {
198 | let result = self.generateId64()
199 | return String(result, radix: 16)
200 | }
201 |
202 | /**
203 | * Generates a 64-bit id as a binary string.
204 | *
205 | * Format: `<41-bits: timestamp><21-bits: node id><2-bits: sequence number>`
206 | * Where timestamp is in milliseconds, minus the epoch.
207 | *
208 | * - Returns: 64-bit id as a binary string
209 | */
210 | @available(iOS, introduced: 1.0)
211 | public func generateId64Bin() -> String {
212 | let result = self.generateId64()
213 | return String(result, radix: 2)
214 | }
215 |
216 | /*----------------------------------------------------------------------*/
217 |
218 | /**
219 | * Extracts the UNIX timestamp from a 64-bit-nil id generated by `generateId64Nil()`.
220 | *
221 | * - Parameter id: the id generated by `generateId64Nil()`
222 | *
223 | * - Returns: the extracted UNIX timestamp in milliseconds
224 | */
225 | @available(iOS, introduced: 1.0)
226 | public static func extractTimestamp64Nil(_ id: UInt64) -> UInt64 {
227 | return SnowflakeIdGenerator.TIMESTAMP_EPOCH + (id >> SnowflakeIdGenerator.SHIFT_TIMESTAMP_64NIL)
228 | }
229 |
230 | /**
231 | * Extracts the UNIX timestamp from a 64-bit-nil id generated by `generateId64NilHex()`.
232 | *
233 | * - Parameter idHex: the id generated by `generateId64NilHex()`
234 | *
235 | * - Returns: the extracted UNIX timestamp in milliseconds
236 | */
237 | @available(iOS, introduced: 1.0)
238 | public static func extractTimestamp64NilHex(_ idHex: String) -> UInt64 {
239 | let id = strtouq(idHex, nil, 16)
240 | return extractTimestamp64Nil(id)
241 | }
242 |
243 | /**
244 | * Extracts the UNIX timestamp from a 64-bit-nil id generated by `generateId64NilBin()`.
245 | *
246 | * - Parameter idBin: the id generated by `generateId64NilBin()`
247 | *
248 | * - Returns: the extracted UNIX timestamp in milliseconds
249 | */
250 | @available(iOS, introduced: 1.0)
251 | public static func extractTimestamp64NilBin(_ idBin: String) -> UInt64 {
252 | let id = strtouq(idBin, nil, 2)
253 | return extractTimestamp64Nil(id)
254 | }
255 |
256 | /**
257 | * Extracts the timestamp part from a 64-bit-nil id generated by `generateId64Nil()` as a Date.
258 | *
259 | * - Parameter id: the id generated by `generateId64Nil()`
260 | *
261 | * - Returns: the extracted timestamp part as a Date
262 | */
263 | @available(iOS, introduced: 1.0)
264 | public static func extractTimestamp64NilAsDate(_ id: UInt64) -> Date {
265 | let timestamp = extractTimestamp64Nil(id)
266 | return Date(timeIntervalSince1970: Double(timestamp) / 1000.0)
267 | }
268 |
269 | /**
270 | * Extracts the timestamp part from a 64-bit-nil id generated by `generateId64NilHex()` as a Date.
271 | *
272 | * - Parameter idHex: the id generated by `generateId64Nil()`
273 | *
274 | * - Returns: the extracted timestamp part as a Date
275 | */
276 | @available(iOS, introduced: 1.0)
277 | public static func extractTimestamp64NilHexAsDate(_ idHex: String) -> Date {
278 | let timestamp = extractTimestamp64NilHex(idHex)
279 | return Date(timeIntervalSince1970: Double(timestamp) / 1000.0)
280 | }
281 |
282 | /**
283 | * Extracts the timestamp part from a 64-bit-nil id generated by `generateId64NilBin()` as a Date.
284 | *
285 | * - Parameter idBin: the id generated by `generateId64NilBin()`
286 | *
287 | * - Returns: the extracted timestamp part as a Date
288 | */
289 | @available(iOS, introduced: 1.0)
290 | public static func extractTimestamp64NilBinAsDate(_ idBin: String) -> Date {
291 | let timestamp = extractTimestamp64NilBin(idBin)
292 | return Date(timeIntervalSince1970: Double(timestamp) / 1000.0)
293 | }
294 |
295 | /**
296 | * Generates a 64-bit-nil id.
297 | *
298 | * Format: `<41-bits: timestamp><23-bits: node id>`
299 | * Where timestamp is in milliseconds, minus the epoch.
300 | *
301 | * - Returns: 64-bit-nil id
302 | */
303 | @available(iOS, introduced: 1.0)
304 | public func generateId64Nil() -> UInt64 {
305 | let result = synchronizd(lock: self) {
306 | let timestamp: UInt64 = DateTimeUtils.currentUnixTimestampMillisec()
307 | if (timestamp == self.lastTimestampMillisec) {
308 | DateTimeUtils.waitTillNextMillisec(timestamp)
309 | return self.generateId64Nil()
310 | } else {
311 | self.lastTimestampMillisec = timestamp
312 | }
313 | let timestampPart = (timestamp - SnowflakeIdGenerator.TIMESTAMP_EPOCH) & SnowflakeIdGenerator.MASK_TIMESTAMP_64NIL
314 | let result = timestampPart << SnowflakeIdGenerator.SHIFT_TIMESTAMP_64NIL | self.template64Nil
315 | return result
316 | }
317 | return result as! UInt64
318 | }
319 |
320 | /**
321 | * Generates a 64-bit-nil id as a hex string
322 | *
323 | * Format: `<41-bits: timestamp><23-bits: node id>`
324 | * Where timestamp is in milliseconds, minus the epoch.
325 | *
326 | * - Returns: 64-bit-nil id as a hex string
327 | */
328 | @available(iOS, introduced: 1.0)
329 | public func generateId64NilHex() -> String {
330 | let result = self.generateId64Nil()
331 | return String(result, radix: 16)
332 | }
333 |
334 | /**
335 | * Generates a 64-bit-nil id as a binary string
336 | *
337 | * Format: `<41-bits: timestamp><23-bits: node id>`
338 | * Where timestamp is in milliseconds, minus the epoch.
339 | *
340 | * - Returns: 64-bit-nil id as a binary string
341 | */
342 | @available(iOS, introduced: 1.0)
343 | public func generateId64NilBin() -> String {
344 | let result = self.generateId64Nil()
345 | return String(result, radix: 2)
346 | }
347 |
348 | /*----------------------------------------------------------------------*/
349 |
350 | /**
351 | * Extracts the UNIX timestamp from a 128-bit id generated by `generateId128Hex()`.
352 | *
353 | * - Parameter idHex: the id generated by `generateId128Hex()`
354 | *
355 | * - Returns: the extracted UNIX timestamp in milliseconds
356 | */
357 | @available(iOS, introduced: 1.0)
358 | public static func extractTimestamp128Hex(_ idHex: String) -> UInt64 {
359 | let index = idHex.index(idHex.endIndex, offsetBy: -16)
360 | let timestamp = strtouq(idHex.substring(to: index), nil, 16)
361 | return timestamp
362 | }
363 |
364 | /**
365 | * Extracts the UNIX timestamp from a 128-bit id generated by `generateId128Bin()`.
366 | *
367 | * - Parameter idBin: the id generated by `generateId128Bin()`
368 | *
369 | * - Returns: the extracted UNIX timestamp in milliseconds
370 | */
371 | @available(iOS, introduced: 1.0)
372 | public static func extractTimestamp128Bin(_ idBin: String) -> UInt64 {
373 | let index = idBin.index(idBin.endIndex, offsetBy: -64)
374 | let timestamp = strtouq(idBin.substring(to: index), nil, 2)
375 | return timestamp
376 | }
377 |
378 | /**
379 | * Extracts the timestamp part from a 128-bit id generated by `generateId128Hex()` as a Date.
380 | *
381 | * - Parameter idHex: the id generated by `generateId128Hex()`
382 | *
383 | * - Returns: the extracted timestamp part as a Date
384 | */
385 | @available(iOS, introduced: 1.0)
386 | public static func extractTimestamp128HexAsDate(_ idHex: String) -> Date {
387 | let timestamp = extractTimestamp128Hex(idHex)
388 | return Date(timeIntervalSince1970: Double(timestamp) / 1000.0)
389 | }
390 |
391 | /**
392 | * Extracts the timestamp part from a 128-bit id generated by `generateId128Bin()` as a Date.
393 | *
394 | * - Parameter idBin: the id generated by `generateId128Bin()`
395 | *
396 | * - Returns: the extracted timestamp part as a Date
397 | */
398 | @available(iOS, introduced: 1.0)
399 | public static func extractTimestamp128BinAsDate(_ idBin: String) -> Date {
400 | let timestamp = extractTimestamp128Bin(idBin)
401 | return Date(timeIntervalSince1970: Double(timestamp) / 1000.0)
402 | }
403 |
404 | /**
405 | * Generates a 128-bit id.
406 | *
407 | * Format: `<64-bits: timestamp><48-bits: node id><16-bits: sequence number>`
408 | * Where timestamp is in milliseconds.
409 | *
410 | * - Returns: 128-bit id as an array of two UInt64s
411 | */
412 | @available(iOS, introduced: 1.0)
413 | public func generateId128() -> [UInt64] {
414 | let result = synchronizd(lock: self) {
415 | let timestamp: UInt64 = DateTimeUtils.currentUnixTimestampMillisec()
416 | var sequence: UInt64 = 0
417 | if (timestamp == self.lastTimestampMillisec) {
418 | // increase sequence
419 | sequence = self.sequenceMillisec.advanced(by: 1)
420 | if (sequence > SnowflakeIdGenerator.MAX_SEQUENCE_128) {
421 | // reset sequence
422 | self.sequenceMillisec = 0
423 | DateTimeUtils.waitTillNextMillisec(timestamp)
424 | return self.generateId128()
425 | } else {
426 | self.sequenceMillisec = sequence
427 | }
428 | } else {
429 | // reset sequence
430 | self.sequenceMillisec = sequence
431 | self.lastTimestampMillisec = timestamp
432 | }
433 | let first = timestamp
434 | let second = (self.nodeId << SnowflakeIdGenerator.SHIFT_NODE_ID_128) | (sequence & SnowflakeIdGenerator.MASK_SEQUENCE_128)
435 | return [first, second]
436 | }
437 | return result as! [UInt64]
438 | }
439 |
440 | /**
441 | * Generates a 128-bit id as a hex string.
442 | *
443 | * Format: `<64-bits: timestamp><48-bits: node id><16-bits: sequence number>`
444 | * Where timestamp is in milliseconds.
445 | *
446 | * - Returns: 128-bit id as a hex string
447 | */
448 | @available(iOS, introduced: 1.0)
449 | public func generateId128Hex() -> String {
450 | let result = self.generateId128()
451 | var tail = String(result[1], radix: 16)
452 | while ( tail.count < 16 ) {
453 | tail.insert("0", at: tail.startIndex)
454 | }
455 | return String(result[0], radix: 16) + tail
456 | }
457 |
458 | /**
459 | * Generates a 128-bit id as a binary string.
460 | *
461 | * Format: `<64-bits: timestamp><48-bits: node id><16-bits: sequence number>`
462 | * Where timestamp is in milliseconds.
463 | *
464 | * - Returns: 128-bit id as a binary string
465 | */
466 | @available(iOS, introduced: 1.0)
467 | public func generateId128Bin() -> String {
468 | let result = self.generateId128()
469 | var tail = String(result[1], radix: 2)
470 | while ( tail.count < 64 ) {
471 | tail.insert("0", at: tail.startIndex)
472 | }
473 | return String(result[0], radix: 2) + tail
474 | }
475 | }
476 |
--------------------------------------------------------------------------------
/SwiftUtils/SyncUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SyncUtils.swift
3 | // SwiftUtils
4 | //
5 | // Created by Thanh Nguyen on 8/15/16.
6 | // Copyright © 2016 Thanh Nguyen. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /**
12 | Synchronizes a code block
13 |
14 | Credit: http://yuhua-chen.logdown.com/posts/253806-synchronized-on-swift
15 |
16 | Usage:
17 |
18 | ```
19 | synchronizd(self) {
20 | .... code here ....
21 | }
22 | ```
23 |
24 | - Parameter lock: object for mutex lock
25 | - Parameter closure: code to execute
26 | - Returns: the returned value from closure
27 | */
28 | @discardableResult public func synchronizd(lock: AnyObject, closure:()->Any?) -> Any? {
29 | objc_sync_enter(lock)
30 | let result = closure()
31 | objc_sync_exit(lock)
32 | return result
33 | }
34 |
--------------------------------------------------------------------------------
/SwiftUtilsPlayground/SwiftUtilsPlayground.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 45;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 5F2BE55F24A0AA94003139DB /* SwiftUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F2BE55E24A0AA94003139DB /* SwiftUtils.framework */; };
11 | 5F2BE56024A0AA94003139DB /* SwiftUtils.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5F2BE55E24A0AA94003139DB /* SwiftUtils.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
12 | 5F3B73331EF291AF00876E32 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F3B73321EF291AF00876E32 /* AppDelegate.swift */; };
13 | 5F3B73351EF291AF00876E32 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F3B73341EF291AF00876E32 /* ViewController.swift */; };
14 | 5F3B73381EF291AF00876E32 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F3B73361EF291AF00876E32 /* Main.storyboard */; };
15 | 5F3B733A1EF291AF00876E32 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5F3B73391EF291AF00876E32 /* Assets.xcassets */; };
16 | 5F3B733D1EF291AF00876E32 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F3B733B1EF291AF00876E32 /* LaunchScreen.storyboard */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXCopyFilesBuildPhase section */
20 | 5F2BE56124A0AA94003139DB /* Embed Frameworks */ = {
21 | isa = PBXCopyFilesBuildPhase;
22 | buildActionMask = 2147483647;
23 | dstPath = "";
24 | dstSubfolderSpec = 10;
25 | files = (
26 | 5F2BE56024A0AA94003139DB /* SwiftUtils.framework in Embed Frameworks */,
27 | );
28 | name = "Embed Frameworks";
29 | runOnlyForDeploymentPostprocessing = 0;
30 | };
31 | /* End PBXCopyFilesBuildPhase section */
32 |
33 | /* Begin PBXFileReference section */
34 | 5F2BE55E24A0AA94003139DB /* SwiftUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; };
35 | 5F3B732F1EF291AF00876E32 /* SwiftUtilsPlayground.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUtilsPlayground.app; sourceTree = BUILT_PRODUCTS_DIR; };
36 | 5F3B73321EF291AF00876E32 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
37 | 5F3B73341EF291AF00876E32 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
38 | 5F3B73371EF291AF00876E32 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
39 | 5F3B73391EF291AF00876E32 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
40 | 5F3B733C1EF291AF00876E32 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
41 | 5F3B733E1EF291AF00876E32 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
42 | 5F3B73441EF291B800876E32 /* SwiftUtilsPlayground.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SwiftUtilsPlayground.entitlements; sourceTree = ""; };
43 | /* End PBXFileReference section */
44 |
45 | /* Begin PBXFrameworksBuildPhase section */
46 | 5F3B732C1EF291AF00876E32 /* Frameworks */ = {
47 | isa = PBXFrameworksBuildPhase;
48 | buildActionMask = 2147483647;
49 | files = (
50 | 5F2BE55F24A0AA94003139DB /* SwiftUtils.framework in Frameworks */,
51 | );
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | /* End PBXFrameworksBuildPhase section */
55 |
56 | /* Begin PBXGroup section */
57 | 5F2BE55D24A0AA94003139DB /* Frameworks */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 5F2BE55E24A0AA94003139DB /* SwiftUtils.framework */,
61 | );
62 | name = Frameworks;
63 | sourceTree = "";
64 | };
65 | 5F3B73261EF291AF00876E32 = {
66 | isa = PBXGroup;
67 | children = (
68 | 5F3B73311EF291AF00876E32 /* SwiftUtilsPlayground */,
69 | 5F3B73301EF291AF00876E32 /* Products */,
70 | 5F2BE55D24A0AA94003139DB /* Frameworks */,
71 | );
72 | sourceTree = "";
73 | };
74 | 5F3B73301EF291AF00876E32 /* Products */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 5F3B732F1EF291AF00876E32 /* SwiftUtilsPlayground.app */,
78 | );
79 | name = Products;
80 | sourceTree = "";
81 | };
82 | 5F3B73311EF291AF00876E32 /* SwiftUtilsPlayground */ = {
83 | isa = PBXGroup;
84 | children = (
85 | 5F3B73441EF291B800876E32 /* SwiftUtilsPlayground.entitlements */,
86 | 5F3B73321EF291AF00876E32 /* AppDelegate.swift */,
87 | 5F3B73341EF291AF00876E32 /* ViewController.swift */,
88 | 5F3B73361EF291AF00876E32 /* Main.storyboard */,
89 | 5F3B73391EF291AF00876E32 /* Assets.xcassets */,
90 | 5F3B733B1EF291AF00876E32 /* LaunchScreen.storyboard */,
91 | 5F3B733E1EF291AF00876E32 /* Info.plist */,
92 | );
93 | path = SwiftUtilsPlayground;
94 | sourceTree = "";
95 | };
96 | /* End PBXGroup section */
97 |
98 | /* Begin PBXNativeTarget section */
99 | 5F3B732E1EF291AF00876E32 /* SwiftUtilsPlayground */ = {
100 | isa = PBXNativeTarget;
101 | buildConfigurationList = 5F3B73411EF291AF00876E32 /* Build configuration list for PBXNativeTarget "SwiftUtilsPlayground" */;
102 | buildPhases = (
103 | 5F3B732B1EF291AF00876E32 /* Sources */,
104 | 5F3B732C1EF291AF00876E32 /* Frameworks */,
105 | 5F3B732D1EF291AF00876E32 /* Resources */,
106 | 5F2BE56124A0AA94003139DB /* Embed Frameworks */,
107 | );
108 | buildRules = (
109 | );
110 | dependencies = (
111 | );
112 | name = SwiftUtilsPlayground;
113 | productName = SwiftUtilsPlayground;
114 | productReference = 5F3B732F1EF291AF00876E32 /* SwiftUtilsPlayground.app */;
115 | productType = "com.apple.product-type.application";
116 | };
117 | /* End PBXNativeTarget section */
118 |
119 | /* Begin PBXProject section */
120 | 5F3B73271EF291AF00876E32 /* Project object */ = {
121 | isa = PBXProject;
122 | attributes = {
123 | LastSwiftUpdateCheck = 0830;
124 | LastUpgradeCheck = 1140;
125 | ORGANIZATIONNAME = "Thanh Nguyen";
126 | TargetAttributes = {
127 | 5F3B732E1EF291AF00876E32 = {
128 | CreatedOnToolsVersion = 8.3.1;
129 | DevelopmentTeam = 7T2WS9KVUZ;
130 | LastSwiftMigration = 1140;
131 | ProvisioningStyle = Automatic;
132 | SystemCapabilities = {
133 | com.apple.Keychain = {
134 | enabled = 1;
135 | };
136 | };
137 | };
138 | };
139 | };
140 | buildConfigurationList = 5F3B732A1EF291AF00876E32 /* Build configuration list for PBXProject "SwiftUtilsPlayground" */;
141 | compatibilityVersion = "Xcode 3.1";
142 | developmentRegion = en;
143 | hasScannedForEncodings = 0;
144 | knownRegions = (
145 | en,
146 | Base,
147 | );
148 | mainGroup = 5F3B73261EF291AF00876E32;
149 | productRefGroup = 5F3B73301EF291AF00876E32 /* Products */;
150 | projectDirPath = "";
151 | projectRoot = "";
152 | targets = (
153 | 5F3B732E1EF291AF00876E32 /* SwiftUtilsPlayground */,
154 | );
155 | };
156 | /* End PBXProject section */
157 |
158 | /* Begin PBXResourcesBuildPhase section */
159 | 5F3B732D1EF291AF00876E32 /* Resources */ = {
160 | isa = PBXResourcesBuildPhase;
161 | buildActionMask = 2147483647;
162 | files = (
163 | 5F3B733D1EF291AF00876E32 /* LaunchScreen.storyboard in Resources */,
164 | 5F3B733A1EF291AF00876E32 /* Assets.xcassets in Resources */,
165 | 5F3B73381EF291AF00876E32 /* Main.storyboard in Resources */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | /* End PBXResourcesBuildPhase section */
170 |
171 | /* Begin PBXSourcesBuildPhase section */
172 | 5F3B732B1EF291AF00876E32 /* Sources */ = {
173 | isa = PBXSourcesBuildPhase;
174 | buildActionMask = 2147483647;
175 | files = (
176 | 5F3B73351EF291AF00876E32 /* ViewController.swift in Sources */,
177 | 5F3B73331EF291AF00876E32 /* AppDelegate.swift in Sources */,
178 | );
179 | runOnlyForDeploymentPostprocessing = 0;
180 | };
181 | /* End PBXSourcesBuildPhase section */
182 |
183 | /* Begin PBXVariantGroup section */
184 | 5F3B73361EF291AF00876E32 /* Main.storyboard */ = {
185 | isa = PBXVariantGroup;
186 | children = (
187 | 5F3B73371EF291AF00876E32 /* Base */,
188 | );
189 | name = Main.storyboard;
190 | sourceTree = "";
191 | };
192 | 5F3B733B1EF291AF00876E32 /* LaunchScreen.storyboard */ = {
193 | isa = PBXVariantGroup;
194 | children = (
195 | 5F3B733C1EF291AF00876E32 /* Base */,
196 | );
197 | name = LaunchScreen.storyboard;
198 | sourceTree = "";
199 | };
200 | /* End PBXVariantGroup section */
201 |
202 | /* Begin XCBuildConfiguration section */
203 | 5F3B733F1EF291AF00876E32 /* Debug */ = {
204 | isa = XCBuildConfiguration;
205 | buildSettings = {
206 | ALWAYS_SEARCH_USER_PATHS = NO;
207 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
208 | CLANG_ANALYZER_NONNULL = YES;
209 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
210 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
211 | CLANG_CXX_LIBRARY = "libc++";
212 | CLANG_ENABLE_MODULES = YES;
213 | CLANG_ENABLE_OBJC_ARC = YES;
214 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
215 | CLANG_WARN_BOOL_CONVERSION = YES;
216 | CLANG_WARN_COMMA = YES;
217 | CLANG_WARN_CONSTANT_CONVERSION = YES;
218 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
219 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
220 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
221 | CLANG_WARN_EMPTY_BODY = YES;
222 | CLANG_WARN_ENUM_CONVERSION = YES;
223 | CLANG_WARN_INFINITE_RECURSION = YES;
224 | CLANG_WARN_INT_CONVERSION = YES;
225 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
226 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
227 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
228 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
229 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
230 | CLANG_WARN_STRICT_PROTOTYPES = YES;
231 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
232 | CLANG_WARN_UNREACHABLE_CODE = YES;
233 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
234 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
235 | COPY_PHASE_STRIP = NO;
236 | DEBUG_INFORMATION_FORMAT = dwarf;
237 | ENABLE_STRICT_OBJC_MSGSEND = YES;
238 | ENABLE_TESTABILITY = YES;
239 | GCC_C_LANGUAGE_STANDARD = gnu99;
240 | GCC_DYNAMIC_NO_PIC = NO;
241 | GCC_NO_COMMON_BLOCKS = YES;
242 | GCC_OPTIMIZATION_LEVEL = 0;
243 | GCC_PREPROCESSOR_DEFINITIONS = (
244 | "DEBUG=1",
245 | "$(inherited)",
246 | );
247 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
248 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
249 | GCC_WARN_UNDECLARED_SELECTOR = YES;
250 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
251 | GCC_WARN_UNUSED_FUNCTION = YES;
252 | GCC_WARN_UNUSED_VARIABLE = YES;
253 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
254 | MTL_ENABLE_DEBUG_INFO = YES;
255 | ONLY_ACTIVE_ARCH = YES;
256 | SDKROOT = iphoneos;
257 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
258 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
259 | SWIFT_VERSION = 4.0;
260 | TARGETED_DEVICE_FAMILY = "1,2";
261 | VALIDATE_PRODUCT = YES;
262 | };
263 | name = Debug;
264 | };
265 | 5F3B73401EF291AF00876E32 /* Release */ = {
266 | isa = XCBuildConfiguration;
267 | buildSettings = {
268 | ALWAYS_SEARCH_USER_PATHS = NO;
269 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
270 | CLANG_ANALYZER_NONNULL = YES;
271 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
272 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
273 | CLANG_CXX_LIBRARY = "libc++";
274 | CLANG_ENABLE_MODULES = YES;
275 | CLANG_ENABLE_OBJC_ARC = YES;
276 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
277 | CLANG_WARN_BOOL_CONVERSION = YES;
278 | CLANG_WARN_COMMA = YES;
279 | CLANG_WARN_CONSTANT_CONVERSION = YES;
280 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
281 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
282 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
283 | CLANG_WARN_EMPTY_BODY = YES;
284 | CLANG_WARN_ENUM_CONVERSION = YES;
285 | CLANG_WARN_INFINITE_RECURSION = YES;
286 | CLANG_WARN_INT_CONVERSION = YES;
287 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
288 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
289 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
290 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
291 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
292 | CLANG_WARN_STRICT_PROTOTYPES = YES;
293 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
294 | CLANG_WARN_UNREACHABLE_CODE = YES;
295 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
296 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
297 | COPY_PHASE_STRIP = NO;
298 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
299 | ENABLE_NS_ASSERTIONS = NO;
300 | ENABLE_STRICT_OBJC_MSGSEND = YES;
301 | ENABLE_TESTABILITY = YES;
302 | GCC_C_LANGUAGE_STANDARD = gnu99;
303 | GCC_NO_COMMON_BLOCKS = YES;
304 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
305 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
306 | GCC_WARN_UNDECLARED_SELECTOR = YES;
307 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
308 | GCC_WARN_UNUSED_FUNCTION = YES;
309 | GCC_WARN_UNUSED_VARIABLE = YES;
310 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
311 | MTL_ENABLE_DEBUG_INFO = NO;
312 | SDKROOT = iphoneos;
313 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
314 | SWIFT_VERSION = 4.0;
315 | TARGETED_DEVICE_FAMILY = "1,2";
316 | VALIDATE_PRODUCT = YES;
317 | };
318 | name = Release;
319 | };
320 | 5F3B73421EF291AF00876E32 /* Debug */ = {
321 | isa = XCBuildConfiguration;
322 | buildSettings = {
323 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
324 | CODE_SIGN_ENTITLEMENTS = SwiftUtilsPlayground/SwiftUtilsPlayground.entitlements;
325 | DEVELOPMENT_TEAM = 7T2WS9KVUZ;
326 | INFOPLIST_FILE = SwiftUtilsPlayground/Info.plist;
327 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
328 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
329 | PRODUCT_BUNDLE_IDENTIFIER = com.github.btnguyen2k.ios.SwiftUtilsPlayground;
330 | PRODUCT_NAME = "$(TARGET_NAME)";
331 | SWIFT_VERSION = 4.0;
332 | };
333 | name = Debug;
334 | };
335 | 5F3B73431EF291AF00876E32 /* Release */ = {
336 | isa = XCBuildConfiguration;
337 | buildSettings = {
338 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
339 | CODE_SIGN_ENTITLEMENTS = SwiftUtilsPlayground/SwiftUtilsPlayground.entitlements;
340 | DEVELOPMENT_TEAM = 7T2WS9KVUZ;
341 | INFOPLIST_FILE = SwiftUtilsPlayground/Info.plist;
342 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
343 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
344 | PRODUCT_BUNDLE_IDENTIFIER = com.github.btnguyen2k.ios.SwiftUtilsPlayground;
345 | PRODUCT_NAME = "$(TARGET_NAME)";
346 | SWIFT_VERSION = 4.0;
347 | };
348 | name = Release;
349 | };
350 | /* End XCBuildConfiguration section */
351 |
352 | /* Begin XCConfigurationList section */
353 | 5F3B732A1EF291AF00876E32 /* Build configuration list for PBXProject "SwiftUtilsPlayground" */ = {
354 | isa = XCConfigurationList;
355 | buildConfigurations = (
356 | 5F3B733F1EF291AF00876E32 /* Debug */,
357 | 5F3B73401EF291AF00876E32 /* Release */,
358 | );
359 | defaultConfigurationIsVisible = 0;
360 | defaultConfigurationName = Release;
361 | };
362 | 5F3B73411EF291AF00876E32 /* Build configuration list for PBXNativeTarget "SwiftUtilsPlayground" */ = {
363 | isa = XCConfigurationList;
364 | buildConfigurations = (
365 | 5F3B73421EF291AF00876E32 /* Debug */,
366 | 5F3B73431EF291AF00876E32 /* Release */,
367 | );
368 | defaultConfigurationIsVisible = 0;
369 | defaultConfigurationName = Release;
370 | };
371 | /* End XCConfigurationList section */
372 | };
373 | rootObject = 5F3B73271EF291AF00876E32 /* Project object */;
374 | }
375 |
--------------------------------------------------------------------------------
/SwiftUtilsPlayground/SwiftUtilsPlayground/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SwiftUtilsPlayground
4 | //
5 | // Created by Thanh Nguyen on 6/15/17.
6 | // Copyright © 2017 Thanh Nguyen. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/SwiftUtilsPlayground/SwiftUtilsPlayground/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/SwiftUtilsPlayground/SwiftUtilsPlayground/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/SwiftUtilsPlayground/SwiftUtilsPlayground/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/SwiftUtilsPlayground/SwiftUtilsPlayground/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/SwiftUtilsPlayground/SwiftUtilsPlayground/SwiftUtilsPlayground.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | keychain-access-groups
6 |
7 | $(AppIdentifierPrefix)com.github.btnguyen2k.ios.SwiftUtilsPlayground
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/SwiftUtilsPlayground/SwiftUtilsPlayground/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // SwiftUtilsPlayground
4 | //
5 | // Created by Thanh Nguyen on 6/15/17.
6 | // Copyright © 2017 Thanh Nguyen. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUtils
11 |
12 | class ViewController: UIViewController {
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 | // Do any additional setup after loading the view, typically from a nib.
17 | }
18 |
19 | override func didReceiveMemoryWarning() {
20 | super.didReceiveMemoryWarning()
21 | // Dispose of any resources that can be recreated.
22 | }
23 |
24 |
25 | @IBOutlet weak var btnTestClicked: UIButton!
26 |
27 | @IBAction func btnTest(_ sender: Any) {
28 | let privKey64 = "MIIEowIBAAKCAQEApjYx0U5cJLAkx3ATqxk5qv7raEv/romlDPa2WFjahplh4lFXD9dzX1fDLWPqwlxp/ZuUlYf3p50oVogoUe2+5IQ/qtpUEaB5Acc8qcx9FekKvxSG8SmCtc3NsC08Bsd6Rw4M6MGB51xeuSbB3cqh7UUhKiFZflUnudMJ7t3mPwEyvgLpA44MJ7nYnG28S0NqTUnM24IVRbPBYvd8xT6swum4nvwGQvbzP8PKqdYd/LBXkCzWMig9ZplGDBNPrAywoWdaGeYIoK4OO3ZhkSKGqAFSEZBj8HYNpHsj3NBqXsRfh0gaAV8tOKP79sBqa6hCL5LQr+nj5QIqYqa4NmUWFQIDAQABAoIBAEQmHmD6Zw9n5XyceIQD0MBKy0y1NH1k1HavRW6N5/OeOiMoRLErHpPuEQmwWRYd/BtOyipRWHqyFlL8esO03jsl+lDcOfIMQPE4tqfMP1FXft9/1CBXa8+NNZPRyDXjXkB6A1L/3Hp3RiNWP9Dnf2MgoAt9JwuEcMhhPkZhV0FnW8SeWh5bdmZstGsoLg6adtR8Bcz0RmE41SCzfIdWNxPUfwuMSitDonTttUOWEvYju8uTSt3VfPcYt8WBNj7ijFwWPRIiVYy118DNpKsmK3ZQQ88diNClPoXbb1XD7FRVhc5x9TqWl7Mpw3T9Wd6O8d+izn2Ny3Om6m0n5CpwJDUCgYEA3cNVvs/aWeqo0ov9QKTNv+XK1LsczAJEfNUa8IPpj35zzy21lC2cOWKKXaQeyIwI9YjeKD7OGdOdynN+Zpv6v+z/+4VNvf5BKWYwBTNaHjeo9gvJY8y49ZUAxXJgT6+wuc+PHVBOtowBed50V3aq+eYcYrkaIeOF9nC7cTcfNo8CgYEAv99SwZHXmxbJOF47LWqk5+k8bhIuPtruU3flW7Aw6tTARXQtf0N1GXPkaiRycFUuTg6YO7YNUK3NyuWKexAoZJ+IsYJp3CZBmUmRMN43xdPRtCTI0iQ7xdxMoyuTiMQTXqWEzSCvMK6LmXA7ss+2RgYGJxRto6OVZDyWsY2y2xsCgYBIoga+hWN12dbLhxA+kPvWo1PokxReeUunO0Ekj3/7AKa7r7PuQXYOkYjSHMP8WWlByj9BHTjx2o6u1V8MCoB5Rg2des64sML05URLH8OfbAmJ9NhUOKRHwzLKeKi00M1oPmQYbMorHfYJzWN7liFv0f9wEkJN4TBqRAgQj8ZIXwKBgQCCwPiPawjH6pZDsIuZXqR0WhBRXlhfgyiIfxsxCXe8gDFq40oiAOFq7/xHjQDjpljA3tdFUcHvKhIk45okVRWBxOoNNbdYTEXF5UlpSPYu/TNaFPs5oXGOW5tqUsbem+grRpaih1R702bUsJQuPbiULYTB2v0AV/j1bwiuhMbO4wKBgGTPf+XhpBdZLMfiuUI0hrVB/QminXMNsw4BU5CDW3K4hzo2fKKVhPkIKZ2RgU0hAqODdyJaEft0wN3X4fq+Tu4KIqz6RR8+BVzfUaAEyUE9E/UHLnO380iQ+7HwkSBGERXgFpZDf++L5sKT+pDT2jZ4tLyE8axIqWTWa22WV8J/"
29 | let pubKey64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApjYx0U5cJLAkx3ATqxk5qv7raEv/romlDPa2WFjahplh4lFXD9dzX1fDLWPqwlxp/ZuUlYf3p50oVogoUe2+5IQ/qtpUEaB5Acc8qcx9FekKvxSG8SmCtc3NsC08Bsd6Rw4M6MGB51xeuSbB3cqh7UUhKiFZflUnudMJ7t3mPwEyvgLpA44MJ7nYnG28S0NqTUnM24IVRbPBYvd8xT6swum4nvwGQvbzP8PKqdYd/LBXkCzWMig9ZplGDBNPrAywoWdaGeYIoK4OO3ZhkSKGqAFSEZBj8HYNpHsj3NBqXsRfh0gaAV8tOKP79sBqa6hCL5LQr+nj5QIqYqa4NmUWFQIDAQAB"
30 |
31 | let tagPublic = "com.github.btnguyen2k.ios.SwiftUtilsPlayground.public"
32 | let tagPrivate = "com.github.btnguyen2k.ios.SwiftUtilsPlayground.private"
33 | let keySecPub = try! RSAUtils.addRSAPublicKey(pubKey64, tagName: tagPublic)
34 | let keySecPriv = try! RSAUtils.addRSAPrivateKey(privKey64, tagName: tagPrivate)
35 | NSLog("Public Key : \(keySecPub!)")
36 | NSLog("Private Key: \(keySecPriv!)")
37 | if keySecPub==nil||keySecPriv==nil{
38 | return
39 | }
40 |
41 | let orgText = "This a string"
42 | NSLog("Original text : \(orgText)")
43 | let secPadding = SecPadding.OAEP
44 |
45 | let eData = RSAUtils.encryptWithRSAKey(orgText.data(using: .utf8)!, rsaKeyRef: keySecPub!, padding: secPadding)
46 | if eData==nil{
47 | NSLog("ERROR: Encrypt with RSA public key")
48 | return
49 | }
50 | NSLog("Encrypted data: \(eData!)")
51 | NSLog("Encrypted data: \(eData!.base64EncodedString())")
52 |
53 | let dData = RSAUtils.decryptWithRSAKey(eData!, rsaKeyRef: keySecPriv!, padding: secPadding)
54 | if dData==nil{
55 | NSLog("ERROR: Decrypt with RSA private key")
56 | return
57 | }
58 | NSLog("Decrypted data: \(dData!)")
59 | NSLog("Decrypted text: \(String(data: dData!, encoding: .utf8)!)")
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/SwiftUtilsTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SwiftUtilsTests/SwiftUtilsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUtilsTests.swift
3 | // SwiftUtilsTests
4 | //
5 | // Created by Thanh Nguyen on 8/14/16.
6 | // Copyright © 2016 Thanh Nguyen. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import SwiftUtils
11 |
12 | class SwiftUtilsTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------