Skip to content

Instantly share code, notes, and snippets.

@mremond
Created November 9, 2018 09:11
Show Gist options
  • Save mremond/7c62e85e2f1ee6ae311ee039b959fcc0 to your computer and use it in GitHub Desktop.
Save mremond/7c62e85e2f1ee6ae311ee039b959fcc0 to your computer and use it in GitHub Desktop.

Revisions

  1. mremond created this gist Nov 9, 2018.
    110 changes: 110 additions & 0 deletions MobileProvision.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,110 @@
    //
    // MobileProvision.swift
    // Fluux.io
    //
    // Created by Mickaël Rémond on 03/11/2018.
    // Copyright © 2018 ProcessOne.
    // Distributed under Apache License v2
    //

    import Foundation

    /* Decode mobileprovision plist file

    Usage:

    1. To get mobileprovision data as embedded in your app:

    MobileProvision.read()

    2. To get mobile provision data from a file on disk:

    MobileProvision.read(from: "my.mobileprovision")

    */

    struct MobileProvision: Decodable {
    var name: String
    var appIDName: String
    var platform: [String]
    var isXcodeManaged: Bool? = false
    var creationDate: Date
    var expirationDate: Date
    var entitlements: Entitlements

    private enum CodingKeys : String, CodingKey {
    case name = "Name"
    case appIDName = "AppIDName"
    case platform = "Platform"
    case isXcodeManaged = "IsXcodeManaged"
    case creationDate = "CreationDate"
    case expirationDate = "ExpirationDate"
    case entitlements = "Entitlements"
    }

    // Sublevel: decode entitlements informations
    struct Entitlements: Decodable {
    let keychainAccessGroups: [String]
    let getTaskAllow: Bool
    let apsEnvironment: Environment

    private enum CodingKeys: String, CodingKey {
    case keychainAccessGroups = "keychain-access-groups"
    case getTaskAllow = "get-task-allow"
    case apsEnvironment = "aps-environment"
    }

    enum Environment: String, Decodable {
    case development, production, disabled
    }

    init(keychainAccessGroups: Array<String>, getTaskAllow: Bool, apsEnvironment: Environment) {
    self.keychainAccessGroups = keychainAccessGroups
    self.getTaskAllow = getTaskAllow
    self.apsEnvironment = apsEnvironment
    }

    init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let keychainAccessGroups: [String] = (try? container.decode([String].self, forKey: .keychainAccessGroups)) ?? []
    let getTaskAllow: Bool = (try? container.decode(Bool.self, forKey: .getTaskAllow)) ?? false
    let apsEnvironment: Environment = (try? container.decode(Environment.self, forKey: .apsEnvironment)) ?? .disabled

    self.init(keychainAccessGroups: keychainAccessGroups, getTaskAllow: getTaskAllow, apsEnvironment: apsEnvironment)
    }
    }
    }

    // Factory methods
    extension MobileProvision {
    // Read mobileprovision file embedded in app.
    static func read() -> MobileProvision? {
    let profilePath: String? = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision")
    guard let path = profilePath else { return nil }
    return read(from: path)
    }

    // Read a .mobileprovision file on disk
    static func read(from profilePath: String) -> MobileProvision? {
    guard let plistDataString = try? NSString.init(contentsOfFile: profilePath,
    encoding: String.Encoding.isoLatin1.rawValue) else { return nil }

    // Skip binary part at the start of the mobile provisionning profile
    let scanner = Scanner(string: plistDataString as String)
    guard scanner.scanUpTo("<plist", into: nil) != false else { return nil }

    // ... and extract plist until end of plist payload (skip the end binary part.
    var extractedPlist: NSString?
    guard scanner.scanUpTo("</plist>", into: &extractedPlist) != false else { return nil }

    guard let plist = extractedPlist?.appending("</plist>").data(using: .isoLatin1) else { return nil }
    let decoder = PropertyListDecoder()
    do {
    let provision = try decoder.decode(MobileProvision.self, from: plist)
    return provision
    } catch {
    // TODO: log / handle error
    return nil
    }
    }
    }