SwiftUI 中未调用 Obj-C 函数

Obj-C function not being called in SwiftUI

我一直在开发一款可以安装 JSON 列表中的任何应用程序的应用程序。控制台中未打印任何错误,但未调用 Obj-C 文件中的方法。

我被允许使用下面的 Obj-C 代码,同时我已经让它在非 SwiftUI 应用程序中工作。我很困惑为什么它在这里不能像在普通 Swift UIKit 应用程序中那样工作。

代码如下。

主视图

struct ContentView: View {
    var installer: AppInstaller?
    @State var externalUrl = ""
    
    var body: some View {
        ZStack(alignment: .top) {

            TrackableScrollView(.vertical, showIndicators: true, contentOffset: $scrollViewContentOffset) {

                VStack(alignment: .trailing, spacing: 15) {
                    VStack(spacing: 10) {
                        ScrollView(.horizontal, content: {
                            HStack(spacing: 10) {
                                ForEach(featuredApps.apps, id: \.id) { app in
                                    FeaturedAppView(image: app.image, download: app.ipa)
                                        .onTapGesture {
                                            
                                            UserDefaults.standard.set(app.name, forKey: "apptitle")
                                            print("Installing App")
                                            
                                            externalUrl = app.ipa
                                            
                                            // Method below doesn't seem to be working
                                            installer?.installApp(withURL: externalUrl) { error in
                                                if error != nil {
                                                    print("App Install Failed.")
                                                }
                                            }
                                        }
                                }
                            }
                            .padding(.leading, 10)
                        }).frame(height: 120)

            }

        }
    }

}

Objective-C 类

//
//  Bridging-header.h
//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

#include "AppInstaller.h"


...

//
//  AppInstaller.m
//  App Installer
//
//  Created by CreatureSurvive on 2017-06-29.
//  Copyright © 2017 Low Budget Animation Studios. All rights reserved.
//

#import "AppInstaller.h"


@implementation AppInstaller {
    NSString *_url;
}

- (void)installAppWithURL:(NSString *)downloadLink completionHandler:(void (^)(NSError *))completion {
    
    // no need to create multiple URLsessions, lets cache this for the lifetime of the app
    static NSURLSession *session;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        session = [NSURLSession sessionWithConfiguration:configuration];
    });

    // create our task
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:[self manifestPostRequestWithURL:downloadLink]
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
                                                {
                                                    // the task completed without error do things
                                                    if (!error)
                                                    {
                                                        // parse the headers into a dictionary so we can get the download link from them
                                                        NSDictionary *headers = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];

                                                        // attempt to download our app
                                                        [self downloadAppWithManifestURL:[@"https://file.io/" stringByAppendingString:headers[@"key"]]];
                                                    }

                                                    // return the completion
                                                    dispatch_async(dispatch_get_main_queue(), ^{
                                                        completion(error);
                                                    });
                                                }];
    // make sure we start the task
    [dataTask resume];
}

- (NSData *)manifestDataWithURL:(NSString *)downloadLink {
    // setup local variables
    NSString *appBundlePath = [[NSBundle mainBundle] pathForResource:@"general" ofType:@"plist"];
    NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // create plist if it doesnt exist in our documents directory;
    if (![fileManager fileExistsAtPath:[documentsDirectory stringByAppendingPathComponent:@"general.plist"]])
    {
        //copy new file to documents directory
        [fileManager copyItemAtPath:appBundlePath toPath:[documentsDirectory stringByAppendingPathComponent:@"general.plist"] error:nil];
    }

    NSString *documentsDirectoryPlistPath = [documentsDirectory stringByAppendingPathComponent:@"general.plist"];

    // check if the our last url is equal to the new url
    if ((_url != nil) &! [_url isEqualToString:downloadLink])
    {
        // no need to update the manifest just return it
        return [fileManager contentsAtPath:documentsDirectoryPlistPath];
    }

    // get the existing manifest as a dictionary to edit
    NSMutableDictionary *manifestDict = [[NSMutableDictionary alloc] initWithContentsOfFile:documentsDirectoryPlistPath];

    // sets the url of the manifest to the requested download link
    manifestDict[@"items"][0][@"assets"][0][@"url"] = downloadLink;
    NSDictionary *apptitle = [[NSUserDefaults standardUserDefaults] objectForKey:@"apptitle"];
    manifestDict[@"items"][0][@"metadata"][@"title"] = apptitle;
    [manifestDict writeToFile:documentsDirectoryPlistPath atomically:YES];

    // return the path to our updated manifest
    return [fileManager contentsAtPath:documentsDirectoryPlistPath];
}


- (NSURLRequest *)manifestPostRequestWithURL:(NSString *)downloadLink
{
    NSString *boundary = [[NSUUID UUID] UUIDString];

    // set body of the request and insert our manifest data
    // this is ugly, but its the only way i've found that works
    NSMutableData *body = [NSMutableData data];
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"Content-Disposition:form-data; name=\"file\"; filename=\"general.plist\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[@"Content-Type: application/x-plist\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[self manifestDataWithURL:downloadLink]];
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];

    // setup request
    NSMutableURLRequest *request = [NSMutableURLRequest new];
    [request setURL:[NSURL URLWithString:@"https://file.io/?expires=1d"]];
    [request setHTTPMethod:@"POST"];
    [request addValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary] forHTTPHeaderField: @"Content-Type"];
    [request setHTTPBody:body];

    return [request copy];
}

- (void)downloadAppWithManifestURL:(NSString *)downloadLink
{
//    NSLog(@"path: %@", downloadLink);
    dispatch_async(dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[@"itms-services://?action=download-manifest&url=" stringByAppendingString:downloadLink]]];
    });
}
@end

...

//
// Created by Dana Buehre on 6/29/17.
// Copyright (c) 2017 Low Budget Animation Studios. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface AppInstaller : NSObject

- (void)installAppWithURL:(NSString *)downloadLink completionHandler:(void (^)(NSError *))completion;

@end

经过进一步检查,我发现我的项目文件中缺少一个 plist。此 plist 是要安装的应用程序的占位符。创建它后,我的问题就解决了。我很抱歉让人们浪费时间试图帮助我。