I would like to release a cocoa touch framework including some json files which contain logic I don't want the framwork's user to see; unfortunately inside the .framework
file there is still the json visible.
I thought about including it in a swift file:
struct JsonProvider { static let json = "..." }
but my json is that large that the file isn't usable any more. I didn't find a solution to command line compile and then include it.
Is there a solution to one of the two problems, i.e.
- hide the json inside the framework or
- precompile a swift file and then add it to the framework?
2 Answers
Answers 1
So I created a JSONEncoder
struct as follows:
public struct JSONEncoder { private static var JSON = "" public static var JSONData = [UInt8]() private static var cipher = [UInt8]() public static var encryptedJSONData = [UInt8]() public static var decryptedJSONData = [UInt8]() private static var decryptedJSON = "" public static func JSONFileToString() { guard let JSONFilePath = Bundle.main.url(forResource: "JSONFile", withExtension:"json"), let cipherFilePath = Bundle.main.url(forResource: "JSONCipher", withExtension:"txt") else { return } do { let text = try String(contentsOf: cipherFilePath, encoding: String.Encoding.utf8) cipher = [UInt8](text.utf8) let data = try Data(contentsOf:JSONFilePath) if let JSON = String(data: data, encoding: String.Encoding.utf8) { self.JSON = JSON JSONData = [UInt8](JSON.utf8) //print("data read: \(JSON)") } else { print("Data to string conversion failed") } } catch { print(error.localizedDescription) } } public static func encodeJSON() { // encrypt bytes for t in JSONData.enumerated() { encryptedJSONData.append(t.element ^ cipher[t.offset]) } print(encryptedJSONData) } public static func decodeJSON() { for t in encryptedJSONData.enumerated() { decryptedJSONData.append(t.element ^ cipher[t.offset]) } decryptedJSON = String(bytes: decryptedJSONData, encoding: String.Encoding.utf8)! //print(decryptedJSON) } }
To set this up you need to add a JSONFile.json
file containing the JSON you want to encode. Add it to copy bundle resources. Repeat this for the cipher text file JSONCipher.txt
. Make sure this file contains no speech marks, as this brings errors and that it is sufficiently large enough to encode the JSON. Don't make it too large as otherwise you get a memory warning.
You can play around with this, to encode a JSON file with XOR, using
JSONEncoder.JSONFileToString() JSONEncoder.encodeJSON()
This will then print to the console, and you can copy this code to a txt file attached to your framework.
For decrypting this file, you can test as follows:
JSONEncoder.decodeJSON()
(Uncommenting the print
statement in decodeJSON
in the JSONEncoder
struct, can test this works)
If your JSON file is too large for this program, you can actually use this tool: https://www.browserling.com/tools/xor-encrypt, and similar XOR Decrypter on the same domain. You can then use the output from this encoder to add the encrypted JSON file to your framework. Just make sure you use the same key when you decode the json!
Note that this stores your text file in the actual project: this is potentially insecure. Maybe host the JSONCipher.txt
file on a server and fetch that using an authenticated access token. The code I provided can be modified to allow for this.
I hope this has pointed you in the right direction for code obfuscation.
Answers 2
You can add json file content to sources as base64 encoded string.
struct JSONFiles { internal static let fileOneBase64String = "iVBORw0KGgoAAAANSUhEUgAAAMEAAADLCAYAAADX..." static var fileOneJSON: [String: Any] { let data = Data(base64Encoded: fileOneBase64String)! return try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any] } }
You can use it later anywhere in you framework by
print(JSONFiles.fileOneJSON)
Hope it help you
UPDATE: Why as base64 encoded string?
The main benefit base64 encoded string - you don't need to escape special symbols contained in JSON string. Don't need to remove new line symbols/intendation symbols from json string before add to swift source file. See example
let string: String = "{ \"name\":\"John\", \"email\": \"hello@stackoverflow\" \"age\":30, \"car\":null }"
But if json is more complex? If it contains 10 nested levels, arrays, dictionaries?
You can cover by UnitTests that value contained in fileOneBase64String
can be decoded to JSON object
import XCTest @testable import MyAwesomeFramework class FrameworkTests: XCTestCase { func testThatBase64EncodedJSONStringCanBeDecoded() { let data = Data(base64Encoded: JSONFiles.fileOneBase64String) XCTAssertNotNil(data, "Unable to convert base64 string to Data") do { _ = try JSONSerialization.jsonObject(with: data, options: []) } catch { XCTFail("Expected decoded JSON ") } } }
0 comments:
Post a Comment