Installation d'un profil de configuration sur iPhone - par programmation

j'aimerais envoyer un profil de configuration avec mon application iPhone, et l'installer si nécessaire.

attention, nous parlons d'un profil de configuration, pas d'un profil d'approvisionnement.

tout d'Abord, une telle tâche est possible. Si vous placez un profil de configuration sur une page Web et cliquez dessus à partir de Safari, Il sera installé. Si vous envoyez un profil par e-mail et cliquez sur la pièce jointe, il s'installera aussi. "Installé" dans ce cas signifie "La installation UI is invoked" - mais je n'ai pas pu aller aussi loin.

J'ai donc travaillé sur la théorie selon laquelle le lancement d'une installation de profil implique la navigation vers celui-ci en tant qu'URL. J'ai ajouté le profil à mon pack d'applications.

A) tout d'abord, j'ai essayé [sharedApp openURL] avec le fichier:// URL dans mon paquet. Pas une telle chance ne se passe rien.

B) j'ai ensuite ajouté une page HTML à mon paquet qui a un lien avec le profil, et l'a chargé dans UIWebView. Cliquer sur le lien ne fait rien. Charger une page identique à partir d'un serveur Web en Safari, cependant, fonctionne très bien - le lien est cliquable, le profil installe. J'ai fourni une délégation UIWebView, répondant oui à chaque demande de navigation - aucune différence.

C) puis j'ai essayé de charger la même page Web à partir de mon paquet en Safari (en utilisant [sharedApp openURL] - rien ne se passe. Je suppose que, Safari Je ne peux pas voir les fichiers dans mon pack d'applications.

D) télécharger la page et le profil sur un serveur Web est faisable, mais une douleur au niveau de l'organisation, sans parler d'une source supplémentaire d'échecs (et si pas de couverture 3G? etc.).

donc ma grande question est: **comment installer un profil de façon programmatique?

et les petites questions sont: qu'est-ce qui peut rendre un lien non-cliquable dans un UIWebView? Est-il possible de charger un fichier:// URL à partir de mon paquet en Safari? Si non, y a-t-il un emplacement local sur iPhone où je peux placer des fichiers et où Safari peut les trouver?

éditer sur B): le problème est en quelque sorte dans le fait que nous lions à un profil. Je l'ai renommé .mobileconfig .xml (parce que c'est vraiment XML), modifié le lien. Et le lien fonctionnait dans mon UIWebView. Renommé en arrière-même chose. Il semble que si UIWebView est réticent à faire des choses à l'échelle de l'application-depuis l'installation du profil ferme l'application. J'ai essayé de lui dire que C'est OK - au moyen de UIWebViewDelegate - mais cela n'a pas convaincu. Même comportement pour mailto: URLs dans UIWebView.

Pour mailto: Url de la technique courante consiste à les traduire en [openURL] appels, mais qui n'a pas assez de travail pour mon cas, reportez-vous au scénario A.

pour itms: URLs, however, UIWebView fonctionne comme prévu...

Edit 2: essayé d'alimenter une URL de données de Safari via [openURL] - ne fonctionne pas, voir ici: données ouvertes iPhone: Url en Safari

EDIT3: trouvé beaucoup d'infos sur la façon Safari ne prend pas en charge les fichiers:// Url. UIWebView, cependant, fait beaucoup. Aussi, Safari sur le simulateur de les ouvrir très bien. Le dernier bit est le plus frustrant.


edit 4: Je n'ai jamais trouvé de solution. Au lieu de cela, j'ai mis en place une interface Web de deux bits où les utilisateurs peuvent commander le profil par e-mail à eux.

67
demandé sur Community 2010-02-26 01:07:19

10 réponses

1) Installer un serveur local comme RoutingHTTPServer



2) Configurer l'en-tête personnalisé:

[httpServer setDefaultHeader:@"Content-Type" value:@"application/x-apple-aspen-config"];

3) Configurer le chemin racine local pour le fichier mobileconfig (Documents):

[httpServer setDocumentRoot:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]];

4) afin de laisser le temps au serveur web pour envoyer le fichier, ajouter ceci :

Appdelegate.h

UIBackgroundTaskIdentifier bgTask;

Appdelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSAssert(self->bgTask == UIBackgroundTaskInvalid, nil);
    bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [application endBackgroundTask:self->bgTask];
            self->bgTask = UIBackgroundTaskInvalid;
        });
    }];
}

5) dans votre contrôleur, appelez safari avec le nom du mobileconfig stocké dans les Documents:

[[UIApplication sharedApplication] openURL:[NSURL URLWithString: @"http://localhost:12345/MyProfile.mobileconfig"]];
36
répondu malinois 2012-03-24 19:44:32

la réponse de malinois a fonctionné pour moi, mais, je voulais une solution qui est revenue à l'application automatiquement après que l'Utilisateur a installé le mobileconfig.

cela m'a pris 4 heures, mais voici la solution, construite sur l'idée des malinois d'avoir un serveur http local: vous renvoyez du HTML à safari qui se rafraîchit; la première fois le serveur renvoie le mobileconfig, et la deuxième fois il renvoie le schéma d'url personnalisé pour revenir à votre application. Le UX est ce que je voulais: le l'application appelle safari, safari ouvre mobileconfig, lorsque l'utilisateur "fait" sur mobileconfig, puis safari charge à nouveau votre application (schéma d'url personnalisé).

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    _httpServer = [[RoutingHTTPServer alloc] init];
    [_httpServer setPort:8000];                               // TODO: make sure this port isn't already in use

    _firstTime = TRUE;
    [_httpServer handleMethod:@"GET" withPath:@"/start" target:self selector:@selector(handleMobileconfigRootRequest:withResponse:)];
    [_httpServer handleMethod:@"GET" withPath:@"/load" target:self selector:@selector(handleMobileconfigLoadRequest:withResponse:)];

    NSMutableString* path = [NSMutableString stringWithString:[[NSBundle mainBundle] bundlePath]];
    [path appendString:@"/your.mobileconfig"];
    _mobileconfigData = [NSData dataWithContentsOfFile:path];

    [_httpServer start:NULL];

    return YES;
}

- (void)handleMobileconfigRootRequest:(RouteRequest *)request withResponse:(RouteResponse *)response {
    NSLog(@"handleMobileconfigRootRequest");
    [response respondWithString:@"<HTML><HEAD><title>Profile Install</title>\
     </HEAD><script> \
     function load() { window.location.href='http://localhost:8000/load/'; } \
     var int=self.setInterval(function(){load()},400); \
     </script><BODY></BODY></HTML>"];
}

- (void)handleMobileconfigLoadRequest:(RouteRequest *)request withResponse:(RouteResponse *)response {
    if( _firstTime ) {
        NSLog(@"handleMobileconfigLoadRequest, first time");
        _firstTime = FALSE;

        [response setHeader:@"Content-Type" value:@"application/x-apple-aspen-config"];
        [response respondWithData:_mobileconfigData];
    } else {
        NSLog(@"handleMobileconfigLoadRequest, NOT first time");
        [response setStatusCode:302]; // or 301
        [response setHeader:@"Location" value:@"yourapp://custom/scheme"];
    }
}

... et voici le code pour l'appeler depuis l'application (viewcontroller):

[[UIApplication sharedApplication] openURL:[NSURL URLWithString: @"http://localhost:8000/start/"]];

J'espère que ça aidera quelqu'un.

26
répondu xaphod 2014-01-09 07:39:18

j'ai écrit une classe pour installer un fichier mobileconfig via Safari et revenir ensuite à l'application. Il s'appuie sur le moteur de serveur http Swifter que j'ai trouvé bien fonctionner. Je veux partager mon code ci-dessous pour le faire. Il est inspiré de plusieurs sources de code que j'ai trouvées flottant dans le www. Donc, si vous trouvez des morceaux de votre propre code, contributions à vous.

class ConfigServer: NSObject {

    //TODO: Don't foget to add your custom app url scheme to info.plist if you have one!

    private enum ConfigState: Int
    {
        case Stopped, Ready, InstalledConfig, BackToApp
    }

    internal let listeningPort: in_port_t! = 8080
    internal var configName: String! = "Profile install"
    private var localServer: HttpServer!
    private var returnURL: String!
    private var configData: NSData!

    private var serverState: ConfigState = .Stopped
    private var startTime: NSDate!
    private var registeredForNotifications = false
    private var backgroundTask = UIBackgroundTaskInvalid

    deinit
    {
        unregisterFromNotifications()
    }

    init(configData: NSData, returnURL: String)
    {
        super.init()
        self.returnURL = returnURL
        self.configData = configData
        localServer = HttpServer()
        self.setupHandlers()
    }

    //MARK:- Control functions

    internal func start() -> Bool
    {
        let page = self.baseURL("start/")
        let url: NSURL = NSURL(string: page)!
        if UIApplication.sharedApplication().canOpenURL(url) {
            var error: NSError?
            localServer.start(listeningPort, error: &error)
            if error == nil {
                startTime = NSDate()
                serverState = .Ready
                registerForNotifications()
                UIApplication.sharedApplication().openURL(url)
                return true
            } else {
                self.stop()
            }
        }
        return false
    }

    internal func stop()
    {
        if serverState != .Stopped {
            serverState = .Stopped
            unregisterFromNotifications()
        }
    }

    //MARK:- Private functions

    private func setupHandlers()
    {
        localServer["/start"] = { request in
            if self.serverState == .Ready {
                let page = self.basePage("install/")
                return .OK(.HTML(page))
            } else {
                return .NotFound
            }
        }
        localServer["/install"] = { request in
            switch self.serverState {
            case .Stopped:
                return .NotFound
            case .Ready:
                self.serverState = .InstalledConfig
                return HttpResponse.RAW(200, "OK", ["Content-Type": "application/x-apple-aspen-config"], self.configData!)
            case .InstalledConfig:
                return .MovedPermanently(self.returnURL)
            case .BackToApp:
                let page = self.basePage(nil)
                return .OK(.HTML(page))
            }
        }
    }

    private func baseURL(pathComponent: String?) -> String
    {
        var page = "http://localhost:\(listeningPort)"
        if let component = pathComponent {
            page += "/\(component)"
        }
        return page
    }

    private func basePage(pathComponent: String?) -> String
    {
        var page = "<!doctype html><html>" + "<head><meta charset='utf-8'><title>\(self.configName)</title></head>"
        if let component = pathComponent {
            let script = "function load() { window.location.href='\(self.baseURL(component))'; }window.setInterval(load, 600);"
            page += "<script>\(script)</script>"
        }
        page += "<body></body></html>"
        return page
    }

    private func returnedToApp() {
        if serverState != .Stopped {
            serverState = .BackToApp
            localServer.stop()
        }
        // Do whatever else you need to to
    }

    private func registerForNotifications() {
        if !registeredForNotifications {
            let notificationCenter = NSNotificationCenter.defaultCenter()
            notificationCenter.addObserver(self, selector: "didEnterBackground:", name: UIApplicationDidEnterBackgroundNotification, object: nil)
            notificationCenter.addObserver(self, selector: "willEnterForeground:", name: UIApplicationWillEnterForegroundNotification, object: nil)
            registeredForNotifications = true
        }
    }

    private func unregisterFromNotifications() {
        if registeredForNotifications {
            let notificationCenter = NSNotificationCenter.defaultCenter()
            notificationCenter.removeObserver(self, name: UIApplicationDidEnterBackgroundNotification, object: nil)
            notificationCenter.removeObserver(self, name: UIApplicationWillEnterForegroundNotification, object: nil)
            registeredForNotifications = false
        }
    }

    internal func didEnterBackground(notification: NSNotification) {
        if serverState != .Stopped {
            startBackgroundTask()
        }
    }

    internal func willEnterForeground(notification: NSNotification) {
        if backgroundTask != UIBackgroundTaskInvalid {
            stopBackgroundTask()
            returnedToApp()
        }
    }

    private func startBackgroundTask() {
        let application = UIApplication.sharedApplication()
        backgroundTask = application.beginBackgroundTaskWithExpirationHandler() {
            dispatch_async(dispatch_get_main_queue()) {
                self.stopBackgroundTask()
            }
        }
    }

    private func stopBackgroundTask() {
        if backgroundTask != UIBackgroundTaskInvalid {
            UIApplication.sharedApplication().endBackgroundTask(self.backgroundTask)
            backgroundTask = UIBackgroundTaskInvalid
        }
    }
}
6
répondu freshking 2015-10-17 08:07:04

je pense que ce que vous recherchez est" L'inscription en direct " en utilisant le protocole D'inscription simple de certificat (SCEP). Consultez le Ota Enrollment Guide et la section" charge utile "du Enterprise Deployment Guide .

selon le aperçu de la configuration du périphérique vous n'avez que quatre options:

  • installation de bureau via USB
  • courriel (pièce jointe)
  • site web (via Safari)
  • inscription et Distribution en direct
4
répondu slf 2010-04-25 15:18:27

cette page explique comment utiliser les images de votre paquet dans UIWebView.

peut-être que la même chose fonctionnerait aussi pour un profil de configuration.

0
répondu Jon-Eric 2010-02-25 22:48:06

avez-vous essayé de simplement avoir l'application Envoyer à l'utilisateur le profil de configuration la première fois qu'il démarre?

-(IBAction)mailConfigProfile {
     MFMailComposeViewController *email = [[MFMailComposeViewController alloc] init];
     email.mailComposeDelegate = self;

     [email setSubject:@"My App's Configuration Profile"];

     NSString *filePath = [[NSBundle mainBundle] pathForResource:@"MyAppConfig" ofType:@"mobileconfig"];  
     NSData *configData = [NSData dataWithContentsOfFile:filePath]; 
     [email addAttachmentData:configData mimeType:@"application/x-apple-aspen-config" fileName:@"MyAppConfig.mobileconfig"];

     NSString *emailBody = @"Please tap the attachment to install the configuration profile for My App.";
     [email setMessageBody:emailBody isHTML:YES];

     [self presentModalViewController:email animated:YES];
     [email release];
}

j'en ai fait une IBAction au cas où vous voudriez l'attacher à un bouton pour que l'utilisateur puisse le renvoyer à lui-même à tout moment. Notez que je n'ai peut-être pas le type MIME correct dans l'exemple ci-dessus, vous devriez vérifier cela.

0
répondu smountcastle 2010-04-21 15:33:38

j'ai bien une autre façon dont cela pourrait fonctionner (malheureusement je n'ai pas un profil de configuration de l'essai):

// Create a UIViewController which contains a UIWebView
- (void)viewDidLoad {
    [super viewDidLoad];
    // Tells the webView to load the config profile
    [self.webView loadRequest:[NSURLRequest requestWithURL:self.cpUrl]];
}

// Then in your code when you see that the profile hasn't been installed:
ConfigProfileViewController *cpVC = 
        [[ConfigProfileViewController alloc] initWithNibName:@"MobileConfigView"
                                                      bundle:nil];
NSString *cpPath = [[NSBundle mainBundle] pathForResource:@"configProfileName"
                                                   ofType:@".mobileconfig"];
cpVC.cpURL = [NSURL URLWithString:cpPath];
// Then if your app has a nav controller you can just push the view 
// on and it will load your mobile config (which should install it).
[self.navigationController pushViewController:controller animated:YES];
[cpVC release];
0
répondu smountcastle 2010-04-22 13:29:25

Je ne sais pas pourquoi vous avez besoin d'un profil de configuration, mais vous pouvez essayer de pirater avec ce délégué de UIWebView:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    if (navigationType == UIWebViewNavigationTypeLinkClicked) {
        //do something with link clicked
        return NO;
    }
    return YES;
}

sinon, vous pouvez considérer activer l'installation à partir d'un serveur sécurisé.

0
répondu Hoang Pham 2011-01-03 10:47:41

Simplement héberger le fichier sur un site web avec l'extension *.mobileconfig et définir le type MIME à application / x-apple-aspen-config. L'utilisateur sera invité, mais si ils acceptent le profil doit être installé.

vous ne pouvez pas installer ces profils par programmation.

0
répondu Brandon 2011-07-14 20:30:17

C'est un grand fil, et surtout le blog mentionné ci-dessus .

pour ceux qui font du Xamarin, voici mon supplément de 2 cents. J'ai intégré le CERT leaf dans mon application en tant que Contenu, puis utilisé le code suivant pour le vérifier:

        using Foundation;
        using Security;

        NSData data = NSData.FromFile("Leaf.cer");
        SecCertificate cert = new SecCertificate(data);
        SecPolicy policy = SecPolicy.CreateBasicX509Policy();
        SecTrust trust = new SecTrust(cert, policy);
        SecTrustResult result = trust.Evaluate();
        return SecTrustResult.Unspecified == result; // true if installed

(Homme, j'aime bien comment nettoyer que le code est, contre soit de la Pomme de langues)

0
répondu Eliot Gillum 2016-11-30 05:30:41