当前位置:  开发笔记 > 前端 > 正文

在iPhone上安装配置文件 - 以编程方式

如何解决《在iPhone上安装配置文件-以编程方式》经验,为你挑选了3个好方法。

我想用我的iPhone应用程序发送配置文件,并在需要时安装它.

请注意,我们谈论的是配置文件,而不是配置文件.

首先,这样的任务是可能的.如果您在网页上放置配置文件并从Safari中单击它,它将被安装.如果您通过电子邮件发送配置文件并单击附件,它也将安装.在这种情况下,"已安装"意味着"调用安装UI" - 但我甚至无法做到这一点.

所以我的理论是,启动配置文件安装涉及以URL的形式导航到它.我将个人资料添加到了我的应用包中.

A)首先,我尝试将[sharedApp openURL]与file:// URL一起放入我的包中.没有这样的运气 - 什么都没发生

B)然后我在我的包中添加了一个HTML页面,其中包含指向配置文件的链接,并将其加载到UIWebView中.点击链接什么都不做.但是,从Safari中的Web服务器加载相同的页面工作正常 - 链接可单击,配置文件安装.我提供了一个UIWebViewDelegate,对每个导航请求回答"是" - 没有区别.

C)然后我尝试从我的包中加载相同的网页在Safari中(使用[sharedApp openURL] - 没有任何反应.我猜,Safari无法看到我的应用包内的文件.

D)在Web服务器上上传页面和配置文件是可行的,但在组织层面上却很痛苦,更不用说额外的失败源(如果没有3G覆盖等等).

所以我的重要问题是:**如何以编程方式安装配置文件?

小问题是:什么可以使UIWebView中的链接不可点击?是否可以在Safari中从我的包中加载file:// URL ?如果没有,iPhone上是否有本地位置可以放置文件,Safari可以找到它们?

编辑B):问题是某种程度上我们正在链接到个人资料.我将它从.mobileconfig重命名为.xml(因为它真的是XML),改变了链接.这个链接在我的UIWebView中工作.重新命名 - 同样的东西.看起来UIWebView似乎不愿意做应用程序范围的事情 - 因为安装配置文件会关闭应用程序.我尝试通过UIWebViewDelegate告诉它没关系 - 但这并没有让人信服.mailto的相同行为:UIWebView中的URL.

对于mailto: URL,常见的技巧是将它们转换为[openURL]调用,但这对我的情况并不适用,请参阅方案A.

对于itms:URL,然而,UIWebView按预期工作...

EDIT2:尝试通过[openURL]向Safari提供数据URL - 不起作用,请参阅此处:iPhone打开数据:Safari中的URL

EDIT3:发现了很多关于Safari如何不支持file:// URL的信息.然而,UIWebView非常有用.此外,模拟器上的Safari打开它们就好了.后一点是最令人沮丧的.


编辑4:我从未找到解决方案.相反,我将一个两位的Web界面组合在一起,用户可以通过电子邮件订购这些配置文件.



1> malinois..:

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)为了让Web服务器有时间发送文件,添加:

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)在您的控制器中,使用存储在Documents中的mobileconfig的名称调用safari:

[[UIApplication sharedApplication] openURL:[NSURL URLWithString: @"http://localhost:12345/MyProfile.mobileconfig"]];


你是否在AppStore destinated app上使用过它?.mobileconfig文件是否已使用带有自签名证书的可信证书o进行签名?我想知道苹果是否可以拒绝安装自签名mobileconfig的应用程序
安装配置文件后,是否可以从野生动物园浏览器重定向回应用程序?

2> xaphod..:

malinois的答案对我有用,但是,我想要一个在用户安装mobileconfig后自动返回应用程序的解决方案.

我花了4个小时,但这里是解决方案,建立在malinois的想法,即拥有一个本地的http服务器:你将HTML返回到自我更新的safari; 第一次服务器返回mobileconfig,第二次返回自定义url-scheme以返回到您的应用程序.用户体验是我想要的:应用程序调用safari,safari打开mobileconfig,当用户在mobileconfig上点击"完成",然后safari再次加载你的应用程序(自定义网址方案).

- (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:@"Profile Install\
     "];
}

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

...这里是从app调用的代码(即viewcontroller):

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

希望这有助于某人.



3> freshking..:

我编写了一个类,用于通过Safari安装mobileconfig文件,然后返回到应用程序.它依赖于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 = "" + "\(self.configName)"
        if let component = pathComponent {
            let script = "function load() { window.location.href='\(self.baseURL(component))'; }window.setInterval(load, 600);"
            page += ""
        }
        page += ""
        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
        }
    }
}


我的Swift 3添加编辑被一些对ios和/或Swift一无所知的聪明人(以及版本2和3之间的差异)拒绝了.我把它放在gist而不是https://gist.github.com/3ph/beb43b4389bd627a271b1476a7622cc5.我知道发布链接反对SO,但显然有些人也是如此.
好的,我解决了我的问题.它只适用于一个方案.将空字符串作为返回URL会导致所描述的行为.使用有效方案将显示对话框,但之后会破坏Safari(取消对话框时).而不是返回.MovedPermanently(self.returnURL),我建议返回一个内有按钮的网页.在错误情况下,用户可以关闭页面.
推荐阅读
mobiledu2402852357
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有