poner - Instalar un perfil de configuración en iPhone-programáticamente
cual es el codigo de iphone (10)
¿Has intentado simplemente que la aplicación envíe al usuario el perfil de configuración la primera vez que se inicia?
-(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]; }
Lo convertí en un IBAction en caso de que quiera vincularlo a un botón para que el usuario pueda volver a enviarlo en cualquier momento. Tenga en cuenta que es posible que no tenga el tipo MIME correcto en el ejemplo anterior, debe verificarlo.
Me gustaría enviar un perfil de configuración con mi aplicación de iPhone e instalarlo si es necesario.
Eso sí, estamos hablando de un perfil de configuración, no de un perfil de aprovisionamiento.
En primer lugar, tal tarea es posible. Si coloca un perfil de configuración en una página web y hace clic en Safari, se instalará. Si envía un perfil por correo electrónico y hace clic en el archivo adjunto, también se instalará. "Instalado" en este caso significa "Se invoca la UI de instalación", pero ni siquiera pude llegar tan lejos.
Así que estaba trabajando bajo la teoría de que iniciar una instalación de perfil implica navegar hacia ella como una URL. Agregué el perfil a mi paquete de aplicaciones.
A) Primero, probé [sharedApp openURL] con el archivo: // URL en mi paquete. No hay tanta suerte, no pasa nada.
B) Luego agregué una página HTML a mi paquete que tiene un enlace al perfil, y lo cargué en un UIWebView. Al hacer clic en el enlace no hace nada. Sin embargo, cargar una página idéntica desde un servidor web en Safari funciona bien: se puede hacer clic en el enlace y se instala el perfil. Proporcioné un UIWebViewDelegate, respondiendo SÍ a cada solicitud de navegación, sin diferencias.
C) Luego traté de cargar la misma página web desde mi paquete en Safari (usando [sharedApp openURL] - no ocurre nada. Supongo que Safari no puede ver los archivos dentro de mi paquete de aplicaciones.
D) Cargar la página y el perfil en un servidor web es factible, pero un dolor a nivel organizacional, por no mencionar una fuente adicional de fallas (¿qué pasa si no hay cobertura 3G, etc.)?
Entonces mi gran pregunta es: ** ¿cómo instalo un perfil programáticamente?
Y las pequeñas preguntas son: ¿qué puede hacer que un enlace no se pueda hacer clic dentro de UIWebView? ¿Es posible cargar un archivo: // URL de mi paquete en Safari? Si no, ¿hay una ubicación local en el iPhone donde puedo colocar los archivos y Safari puede encontrarlos?
EDIT en B): el problema está de alguna manera en el hecho de que estamos enlazando a un perfil. Cambié el nombre de .mobileconfig a .xml (porque es realmente XML), alteré el enlace. Y el enlace funcionó en mi UIWebView. Lo renombré, lo mismo. Parece que UIWebView es reacio a hacer todo lo relacionado con la aplicación, ya que la instalación del perfil cierra la aplicación. Intenté decirle que está bien, por medio de UIWebViewDelegate, pero eso no convenció. Mismo comportamiento para mailto: URL dentro de UIWebView.
Para mailto: URLs, la técnica común es traducirlas a llamadas [openURL], pero eso no funciona para mi caso, ver el escenario A.
Para itms: URL, sin embargo, UIWebView funciona como se esperaba ...
EDIT2: intentado alimentar una URL de datos a Safari a través de [openURL] - no funciona, mira aquí: iPhone Open DATA: Url en Safari
EDIT3: encontró mucha información sobre cómo Safari no es compatible con file: // URLs. UIWebView, sin embargo, hace mucho. Además, Safari en el simulador los abre muy bien. El último bit es el más frustrante.
EDIT4: nunca encontré una solución. En su lugar, armé una interfaz web de dos bits donde los usuarios pueden pedir el perfil enviado por correo electrónico.
1) Instale un servidor local como RoutingHTTPServer
2) Configure el encabezado personalizado:
[httpServer setDefaultHeader:@"Content-Type" value:@"application/x-apple-aspen-config"];
3) Configure la ruta raíz local para el archivo mobileconfig (Documentos):
[httpServer setDocumentRoot:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]];
4) Para permitirle tiempo al servidor web para enviar el archivo, agregue esto:
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) En su controlador, llame a safari con el nombre de la configuración móvil almacenada en Documentos:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: @"http://localhost:12345/MyProfile.mobileconfig"]];
Creo que lo que está buscando es "Inscripción en el aire" utilizando el Protocolo simple de inscripción de certificados (SCEP). Eche un vistazo a la Guía de inscripción de OTA y a la sección Carga útil de SCEP de la Guía de implementación empresarial .
De acuerdo con la descripción de la configuración del dispositivo , solo tiene cuatro opciones:
- Instalación de escritorio a través de USB
- Adjunto de correo electrónico)
- Sitio web (a través de Safari)
- Matrícula y distribución por aire
Escribí una clase para instalar un archivo de configuración móvil a través de Safari y luego volví a la aplicación. Se basa en el motor de servidor http Swifter que me pareció estar funcionando bien. Quiero compartir mi código a continuación para hacer esto. Está inspirado en múltiples fuentes de código que encontré flotando en el www. Entonces, si encuentras pedazos de tu propio código, contribuciones para ti.
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
}
}
}
Este es un gran hilo, y especialmente el blog mencionado anteriormente .
Para aquellos que hacen Xamarin, aquí están mis 2 centavos añadidos. Inserté el certificado de hoja en mi aplicación como contenido, luego usé el siguiente código para verificarlo:
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
(Hombre, me encanta lo limpio que es ese código, frente a cualquiera de los idiomas de Apple)
He pensado en otra forma en la que podría funcionar (lamentablemente no tengo un perfil de configuración para probar):
// 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];
La respuesta de malinois funcionó para mí, PERO, quería una solución que volviera a la aplicación automáticamente después de que el usuario instaló el dispositivo móvil.
Me tomó 4 horas, pero aquí está la solución, basada en la idea de malinois de tener un servidor http local: devuelve HTML a safari que se actualiza; la primera vez que el servidor devuelve la configuración de dispositivo móvil, y la segunda vez que devuelve el esquema de URL personalizado para volver a su aplicación. El UX es lo que yo quería: la aplicación llama safari, Safari abre mobileconfig, cuando el usuario toca "listo" en mobileconfig, Safari carga su aplicación nuevamente (esquema de url personalizado).
- (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"];
}
}
... y aquí está el código para llamar a esto desde la aplicación (es decir, viewcontroller):
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: @"http://localhost:8000/start/"]];
Espero que esto ayude a alguien.
No estoy seguro de por qué necesita un perfil de configuración, pero puede intentar piratear con este delegado desde UIWebView:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
if (navigationType == UIWebViewNavigationTypeLinkClicked) {
//do something with link clicked
return NO;
}
return YES;
}
De lo contrario, puede considerar habilitar la instalación desde un servidor seguro.
Simplemente aloje el archivo en un sitio web con la extensión * .mobileconfig y establezca el tipo MIME en application / x-apple-aspen-config. Se le preguntará al usuario, pero si aceptan, el perfil debe estar instalado.
No puede instalar estos perfiles mediante programación.
Esta página explica cómo usar imágenes de su paquete en UIWebView.
Quizás lo mismo funcionaría para un perfil de configuración también.