Установка профиля конфигурации на iPhone - программно

Я хотел бы отправить профиль конфигурации с моим приложением для iPhone и установить его при необходимости.

Имейте в виду, мы говорим о профиле конфигурации, а не о профиле обеспечения.

Во-первых, такая задача возможна. Если вы разместите профиль конфигурации на веб-странице и щелкните по нему в Safari, он будет установлен. Если вы отправите профиль по электронной почте и нажмете вложение, оно также будет установлено. «Установлено» в данном случае означает «Пользовательский интерфейс установки вызывается» - но я даже не смог зайти так далеко.

Итак, я работал в соответствии с теорией, согласно которой установка профиля требует перехода к нему в виде URL-адреса. Я добавил профиль в свой пакет приложений.

A) Сначала я попробовал [sharedApp openURL] с URL-адресом file: //в моем комплекте. Нет такой удачи - ничего не происходит.

B) Затем я добавил HTML-страницу в свой пакет, которая имеет ссылку на профиль, и загрузил ее в UIWebView. Нажатие на ссылку ничего не делает. Однако загрузка идентичной страницы с веб-сервера в Safari работает нормально - ссылка кликабельна, профиль устанавливается. Я предоставил UIWebViewDelegate, отвечая ДА на каждый запрос навигации - без разницы.

C) Затем я попытался загрузить ту же самую веб-страницу из своего пакета в Safari (используя [sharedApp openURL] - ничего не происходит. Я думаю, Safari не может видеть файлы внутри моего пакета приложения.

D) . Загрузка страницы и профиля на веб-сервер выполнима, но на организационном уровне это проблема, не говоря уже о дополнительном источнике сбоев (что если нет покрытия 3G? и т. д.). ).

Итак, мой главный вопрос: ** как мне установить профиль программно?

И вот маленькие вопросы: что может сделать ссылку недоступной для клика в UIWebView? Можно ли загрузить файл: //URL из пакета my в Safari? Если нет, есть ли локальное местоположение на iPhone, где я могу разместить файлы, и Safari может их найти?

РЕДАКТИРОВАТЬ на B): проблема в том, что мы ссылаемся на профиль. Я переименовал его из .mobileconfig в .xml (потому что это действительно XML), изменил ссылку. И ссылка работала в моем UIWebView. Переименовал обратно - тоже самое. Похоже, что UIWebView неохотно делает вещи для всего приложения - так как установка профиля закрывает приложение. Я пытался сказать, что все в порядке - с помощью UIWebViewDelegate - но это не убедило. Такое же поведение для mailto: URL в UIWebView.

Для адресов mailto: обычная техника - преобразовать их в вызовы [openURL], но это не совсем подходит для моего случая, см. сценарий А.

Для itms: URL-адреса, однако, UIWebView работает должным образом ...

EDIT2: попытался передать URL-адрес данных в Safari через [openURL] - не работает, см. здесь: iPhone Открытые данные: URL в Safari

EDIT3: нашел много информации о том, как Safari не поддерживает file: //URL. UIWebView, однако, очень много делает. Также в Safari на симуляторе их открывают просто отлично. Последний бит является самым расстраивающим.


EDIT4: я не нашел решения. Вместо этого я собрал двухбитный веб-интерфейс, где пользователи могут заказать профиль, отправленный им по электронной почте.

69 голосов | спросил Seva Alekseyev 26 FebruaryEurope/MoscowbFri, 26 Feb 2010 01:07:19 +0300000000amFri, 26 Feb 2010 01:07:19 +030010 2010, 01:07:19

10 ответов


0

1) Установите локальный сервер, например RoutingHTTPServer

2) Настройте пользовательский заголовок:

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

3) Настройте локальный корневой путь для файла mobileconfig (Documents):

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

4) Чтобы дать время веб-серверу отправить файл, добавьте следующее:

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) В вашем контроллере вызовите safari с именем mobileconfig, хранящимся в Documents:

[[UIApplication sharedApplication] openURL:[NSURL URLWithString: @"http://localhost:12345/MyProfile.mobileconfig"]];
ответил malinois 24 MarpmSat, 24 Mar 2012 23:44:32 +04002012-03-24T23:44:32+04:0011 2012, 23:44:32
0

Ответ от малинуа сработал для меня, НО, я хотел решение, которое вернулось в приложение автоматически после того, как пользователь установил mobileconfig.

Это заняло у меня 4 часа, но вот решение, основанное на идее Малинуа о наличии локального http-сервера: вы возвращаете HTML в safari, который обновляется; первый раз, когда сервер возвращает mobileconfig, и второй раз, когда он возвращает пользовательскую схему URL, чтобы вернуться в ваше приложение. UX - это то, что я хотел: приложение вызывает safari, safari открывает mobileconfig, когда пользователь нажимает «done» на mobileconfig, а затем safari снова загружает ваше приложение (пользовательская схема URL).

- (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"];
    }
}

... а вот код для вызова этого из приложения (т.е. viewcontroller):

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

Надеюсь, это кому-нибудь поможет.

ответил xaphod 9 Jam1000000amThu, 09 Jan 2014 11:39:18 +040014 2014, 11:39:18
0

Я написал класс для установки файла mobileconfig через Safari, а затем для возврата в приложение. Он опирается на механизм http-сервера Swifter , который, как мне показалось, работает хорошо. Я хочу поделиться своим кодом ниже для этого. Он вдохновлен множественными источниками кода, которые я нашел плавающими на www. Так что, если вы найдете части своего собственного кода, вклад в вас.

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
        }
    }
}
ответил freshking 17 +03002015-10-17T11:07:04+03:00312015bEurope/MoscowSat, 17 Oct 2015 11:07:04 +0300 2015, 11:07:04
0

Я думаю, что вы ищете «беспроводную регистрацию» с использованием простого протокола регистрации сертификатов (SCEP). Ознакомьтесь с Руководством по регистрации OTA и разделом «Полезная нагрузка SCEP» в Руководство по развертыванию предприятия .

Согласно обзору конфигурации устройств , у вас есть только четыре варианта

  • Установка на рабочий стол через USB
  • электронная почта (вложение)
  • Веб-сайт (через Safari)
  • Регистрация и распространение через эфир
ответил slf 25 PMpSun, 25 Apr 2010 19:12:14 +040012Sunday 2010, 19:12:14
0

Эта страница объясняет, как использовать изображения из вашего пакета в UIWebView .

Возможно, то же самое будет работать и для профиля конфигурации.

ответил Jon-Eric 26 FebruaryEurope/MoscowbFri, 26 Feb 2010 01:48:06 +0300000000amFri, 26 Feb 2010 01:48:06 +030010 2010, 01:48:06
0

Пытались ли вы, чтобы приложение отправляло пользователю по почте профиль конфигурации при первом запуске?

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

     [email setSubject: @ "Профиль конфигурации моего приложения"];

     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 = @ "Пожалуйста, нажмите на вложение, чтобы установить профиль конфигурации для Моего приложения.";
     [email setMessageBody: emailBody isHTML: YES];

     [self presentModalViewController: анимированная электронная почта: ДА];
     [релиз по электронной почте];
}

Я сделал это IBAction на тот случай, если вы хотите привязать его к кнопке, чтобы пользователь мог повторно отправить его себе в любое время. Обратите внимание, что в приведенном выше примере у меня может быть неправильный тип MIME, вам следует это проверить.

ответил smountcastle 21 PMpWed, 21 Apr 2010 19:33:38 +040033Wednesday 2010, 19:33:38
0

У меня есть другой способ, которым он может работать (к сожалению, у меня нет профиля конфигурации для тестирования):

//Создаем UIViewController, который содержит UIWebView
- (void) viewDidLoad {
    [super viewDidLoad];
    //Сообщает webView загрузить профиль конфигурации
    [self.webView loadRequest: [NSURLRequest requestWithURL: self.cpUrl]];
}

//Затем в вашем коде, когда вы видите, что профиль не был установлен:
ConfigProfileViewController * cpVC =
        [[ConfigProfileViewController alloc] initWithNibName: @ "MobileConfigView"
                                                      расслоение: ноль];
NSString * cpPath = [[NSBundle mainBundle] pathForResource: @ "configProfileName"
                                                   OfType: @ "mobileconfig"];
cpVC.cpURL = [NSURL URLWithString: cpPath];
//Затем, если в вашем приложении есть навигационный контроллер, вы можете просто нажать на представление
//и он загрузит ваш мобильный конфиг (который должен его установить).
[self.navigationController pushViewController: контроллер анимирован: ДА];
[релиз cpVC];
ответил smountcastle 22 PMpThu, 22 Apr 2010 17:29:25 +040029Thursday 2010, 17:29:25
0

Не знаю, зачем вам нужен профиль конфигурации, но вы можете попытаться взломать этот делегат из UIWebView:

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

В противном случае вы можете включить установку с безопасного сервера.

ответил Hoang Pham 3 Jpm1000000pmMon, 03 Jan 2011 13:47:41 +030011 2011, 13:47:41
0

Просто разместите файл на веб-сайте с расширением * .mobileconfig и установите тип MIME для application /x-apple-aspen-config. Пользователю будет предложено, но если они принимают, профиль должен быть установлен.

Вы не можете установить эти профили программно.

ответил Brandon 15 J000000Friday11 2011, 00:30:17
0

Это отличная тема, особенно блог , упомянутый выше .

Для тех, кто делает Xamarin, вот мои добавленные 2 цента. Я встроил листовой сертификат в свое приложение как Контент, а затем использовал следующий код для его проверки:

        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

(Чувак, мне нравится, насколько чист этот код по сравнению с любым из языков Apple)

ответил Eliot Gillum 30 32016vEurope/Moscow11bEurope/MoscowWed, 30 Nov 2016 08:30:41 +0300 2016, 08:30:41

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132