Swift-based iOS App Extension using NSExtension - ios

I am experimenting with daemon processes on iOS using the NSExtension private API.
I have been following Ian McDowell's article Multi-Process iOS App Using NSExtension. A sample project is available for download at the end of the article.
My goal is to port this sample project from Objective-C to Swift. It has been simple enough porting the AppDelegate and ViewController to swift for the host application. XCode 9.2 seems to map the NSExtension method declarations in PrivateHeaders.h to Swift for use.
PrivateHeaders.h
#interface NSExtension : NSObject
+ (instancetype)extensionWithIdentifier:(NSString *)identifier error:(NSError **)error;
- (void)beginExtensionRequestWithInputItems:(NSArray *)inputItems completion:(void (^)(NSUUID *requestIdentifier))completion;
- (int)pidForRequestIdentifier:(NSUUID *)requestIdentifier;
- (void)cancelExtensionRequestWithIdentifier:(NSUUID *)requestIdentifier;
- (void)setRequestCancellationBlock:(void (^)(NSUUID *uuid, NSError *error))cancellationBlock;
- (void)setRequestCompletionBlock:(void (^)(NSUUID *uuid, NSArray *extensionItems))completionBlock;
- (void)setRequestInterruptionBlock:(void (^)(NSUUID *uuid))interruptionBlock;
#end
I run into trouble when trying to port the application extension class from Objective-C to Swift. Below is a simplification of the original class and my port:
Extension.h
#import <Foundation/Foundation.h>
#interface Extension : NSObject <NSExtensionRequestHandling>
#end
Extension.m
#import "Extension.h"
#implementation Extension
- (void)beginRequestWithExtensionContext:(NSExtensionContext *)context {
NSLog(#"Beginning request with context: %#", [context description]);
}
#end
Extension.swift
import Foundation
class Extension: NSObject, NSExtensionRequestHandling {
func beginRequest(with context: NSExtensionContext) {
print("Beginning request with context: \(context.description)")
}
}
Attempting to launch the Swift Extension process result in the following error in XCode:
Unable to setup extension context - error: Couldn't communicate with a helper application.
Inspecting the iPhone log via Apple Configurator 2 reveals the following error:
iPhone Extension(CoreFoundation)[366] : *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** - [__NSDictionaryM setObject:forKey:]: object cannot be nil
Is anyone able to provide any additional insight into what might be going wrong here? The swap from Objective-C to Swift seems to be straightforward. I cannot even hit a breakpoint on the Extension application before it seems to crash.
Note: I understand that this likely wouldn't make it past app store review. It will never be going near the app store.

Related

Receiver '**' for class message is a forward declaration Error. Swift Static Library use in Objective-C

I am trying to make a Swift Static library and apply it to Swift and Objective Project.
import Foundation
#objc open class Library001_Test: NSObject {
public override init(){}
#objc public func testPrint() {
print("My Name is Andi")
}
#objc public func getUUID(userName: String) -> String {
let uuid = UUID().uuidString
return "\(userName)'s UUID : \(uuid)"
}
}
I wrote the code like this using Swift.
And in the Edit Scheme menu, I changed the Build Configuration to Release and proceeded with Run. As a result, the 'libLibrary001.a' file and the 'Library001.swiftmodule' folder were created.
These two artifacts work well when pasted into a Swift project and imported.
But the problem is an Objective-C project.
I put both artifacts into my project and checked:
[General - Frameworks, Libraries. and Embedded Content] whether the library is recognized
Whether the library is recognized in [Build Phases - Link Binary With Libraries]
Check [Build Settings - Library Search Paths] address
Defines Module - Yes
And I put '#class Library001_Test;' in ViewController.h
#import <UIKit/UIKit.h>
#class Library001_Test;
#interface ViewController : UIViewController
#end
And in ViewController.m, '#import "ProductName-Swift.h" and the created Class were loaded.
#import "ViewController.h"
#import "SwiftInObjectiveC-Swift.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Library001_Test *test = [[Library001_Test alloc] init];
}
#end
error : Receiver 'Library001_Test' for class message is a forward declaration
error : Receiver type 'Library001_Test' for instance message is a forward declaration
An error occurred while doing this. I've tried all the methods I've found on the internet and I'm wondering where the problem is.
Is the code the problem? Did I not set it up well??
The Swift file created in the project is import well in Objective-C... Why the hell is the .a file not working like this?
My problem was with '(ProductName)-Swift.h'
If you look at how Swift Code is used in Objective-C, many articles say to import (ProductName)-Swift.h. So I only added the project header that I want to apply, but I also need to add the product header made from the library.
My problem was simple, but it took me a long time to figure it out. The error was not found 'Class' and 'func' in Swift static library. My workaround was resolved using the (LibraryProductName)-Swift.h of the library I created, rather than the (ProductName)-Swift.h of the project you are working on.
If you refer to the address below, you can prevent the error that occurred in advance.
https://medium.com/#mail2ashislaha/swift-objective-c-interoperability-static-libraries-modulemap-etc-39caa77ce1fc

Firebase Crashlytics MacOS - all crashes under AppKit

I have an iOS app that is ported to MacOS. The app uses Firebase for Crashlytics. So far I managed to configure everything just fine, by creating a separate Mac target and separate Firebase project for that target. The problem is that the crashes I see in the console for the MacOS project are all under "AppKit". Example:
AppKit | -[NSApplication _crashOnException:] + 106
Not very informative, is it... Now, I can still get the crashing exception if I inspect the crashes and then go to 'Keys':
crash_info_entry_0 | Crashing on exception: *** -[__NSCFCalendar rangeOfUnit:startDate:interval:forDate:]: date cannot be nil
But with that all the different crashes are grouped together under that AppKit crash and so it is not very helpful.
I realise that this issue is due to the default behaviour of AppKit catching all exceptions on MacOS by default. Is there perhaps a better way to setup Crashlytics for MacOS, in order to get more granular reports, like on iOS and other platforms?
After a lot of research I found that there is no perfect solution to this. I tried overriding NSApplication and setting it as NSPrincipalClass, and even implemented Sentry instead - no success. But I found a way to bypass AppKit using method swizzling and FIRExceptionModel.
Note: Before anything, for Firebase Crashlytics to work on MacOS, you need the following in your AppDelegate's didFinishLaunchingWithOptions:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults registerDefaults:#{#"NSApplicationCrashOnExceptions" : #"YES"}];
Then you need to create a category of NSApplication and swizzle the method _crashOnException:
#import <objc/runtime.h>
#import "NSApplication+CrashReport.h"
#import <FIRCrashlytics.h>
#implementation NSApplication (CrashReport)
+(void)load {
static dispatch_once_t once_token;
dispatch_once(&once_token, ^{
SEL crashOnExceptionSelector = #selector(_crashOnException:); // Ignore 'Undeclared selector' warning.
SEL crashOnExceptionReporterSelector = #selector(reported__crashOnException:);
Method originalMethod = class_getInstanceMethod(self, crashOnExceptionSelector);
Method extendedMethod = class_getInstanceMethod(self, crashOnExceptionReporterSelector);
method_exchangeImplementations(originalMethod, extendedMethod);
});
}
- (void)reported__crashOnException:(NSException*)exception {
NSArray<NSString*> *stacktrace = [exception callStackSymbols];
[[FIRCrashlytics crashlytics]setCustomValue:stacktrace forKey:#"mac_os_stacktrace"];
FIRExceptionModel *errorModel = [FIRExceptionModel exceptionModelWithName:exception.name reason:exception.reason];
// The below stacktrace is hardcoded as an example, in an actual solution you should parse the stacktrace array entries.
errorModel.stackTrace = #[
[FIRStackFrame stackFrameWithSymbol:#"This stacktrace is fabricated as a proof of concept" file:#"Hello from Serge" line:2021],
[FIRStackFrame stackFrameWithSymbol:#"__exceptionPreprocess" file:#"CoreFoundation" line:250],
[FIRStackFrame stackFrameWithSymbol:#"objc_exception_throw" file:#"libobjc.A.dylib" line:48],
[FIRStackFrame stackFrameWithSymbol:#"-[__NSCFCalendar rangeOfUnit:startDate:interval:forDate:]" file:#"CoreFoundation" line:453]
];
// Note: ExceptionModel will always be reported as a non-fatal.
[[FIRCrashlytics crashlytics] recordExceptionModel:errorModel];
[self reported__crashOnException:exception];
}
#end
This code as a gist: https://gist.github.com/sc941737/c0c4542401ce203142c93ddc9b05eb1f
This means however that the exceptions won't be reported as crashes, but as non-fatals. So I would recommend setting an extra custom key to filter crashes from non-fatals more easily. See Firebase docs for details: https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=ios
NOTE: Swizzling private methods of Apple's APIs is a no-no for apps targeting the app store. It wasn't an issue in my case, because it was an app used internally.

iTunes Store Operation failed - the app references non-public [duplicate]

I am getting this warning while submitting app to the Apps store through organizer.
The app references non-public selectors in Payload/.app/: decoder
i know we get this warning if we use any Third Party API in our application. I have used SOCKETIO-ObjC library for chat functionality in application. Also used facebook iOS sdk for fb implementation.So i am not getting exactly what causes this warning.! Please find attached ScreenShot for better understanding
You may get this warning just for using a selector in your own code or third party code that has the same name as some selector that is marked as non-public. Happens to me all the time. Never got rejected for it.
By "same name" i mean just something as simple as you having an object with this selector:
-(id) XYZKMyClass doSomethingFancy:(id) toThis
...and there being a selector like this for an internal Apple functionality
-(id) ApplesClass doSomethingFancy:(id) toSomething
So: What it seems they are looking for is the signature -(id) doSomethingFancy:(id). You can see how it's very easy to accidentally bump up against this.
Presumably they perform a deeper check at the App Store Police HQ, and determine that the flagged selector is in your code, and hence OK.
This can help you:
Before:
#import "SocketIOJSONSerialization.h"
extern NSString * const SocketIOException;
// covers the methods in SBJson and JSONKit
#interface NSObject (SocketIOJSONSerialization)
// used by both JSONKit and SBJson
- (id) objectWithData:(NSData *)data;
// Use by JSONKit serialization
- (NSString *) JSONString;
**- (id) decoder;**
// Used by SBJsonWriter
- (NSString *) stringWithObject:(id)object;
#end
After:
#import "SocketIOJSONSerialization.h"
extern NSString * const SocketIOException;
// covers the methods in SBJson and JSONKit
#interface NSObject (SocketIOJSONSerialization)
// used by both JSONKit and SBJson
- (id) objectWithData:(NSData *)data;
// Use by JSONKit serialization
- (NSString *) JSONString;
**- (id) jsonDecoder;**
// Used by SBJsonWriter
- (NSString *) stringWithObject:(id)object;
#end
I get in this link: http://blog.csdn.net/erica_sadun/article/details/12188083
Check your Target Membership for all classes used in project. In some cases when you create or copy your target the warning may appears without link error.

Obj-c extension does not work in swift

Trying to use a obj-c extension in my swift project:
UIBezierPath+hello.h
#import <UIKit/UIKit.h>
#interface UIBezierPath(hello)
- (void)hello;
#end
UIBezierPath+hello.m
#import "UIBezierPath+hello.h"
#implementation UIBezierPath(hello)
- (void)hello
{
NSLog (#"hello hello");
}
#end
Bridging-Header.h
#import "UIBezierPath+hello.h"
Swift
let helloPath = UIBezierPath()
helloPath.hello()
It does build and it does sees the hello method. But it does crush:
-[UIBezierPath hello]: unrecognized selector sent to instance 0x7d2116d0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIBezierPath hello]:
unrecognized selector sent to instance 0x7d2116d0'
It seems it does not recognises the implementation.
Probably it does not work because I am dumb:)
Custom obj-c classes work in swift. Only extension give me this problem
Thanks guys
That probably means that you forgot to tick the "Target" checkbox when adding "UIBezierPath+hello.m" to the project, so that the file is not compiled and linked
to the executable.
Adding the "Target Membership" in the File inspector should solve the problem.

Xcode Admob singleton - "use of undeclared identifier "shared"" error

Okay first post ever on any forum but i'll do my best to describe my problem. I'm a beginner at Xcode so i'm sorta expecting an easy solution, but i just can't seem to figure this one out.
In Xcode i am trying to create an Admob singleton to get admob in all my view controllers. Admob will back up iAd, which will be showed should Ad fail.
I followed this guide: http://googleadsdeveloper.blogspot.dk/2012/04/creating-gadbannerview-singleton-in.html
I created a GADMasterViewController .m and .h file
The GADMasterViewController.h looks like this
#import "GADBannerView.h"
#interface GADMasterViewController : UIViewController <GADBannerViewDelegate> {
GADBannerView *adbanner_;
BOOL isLoaded_;
id currentDelegate_;
}
#end
and the GADMasterViewController.m file looks exactly as the one in the guide except i put #import "GADMasterViewController.h" at the top.
Then in my viewController.m where i want the ad to show i put
- (void)bannerView:(GADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error{
_UIiAd.hidden = YES;
shared = [GADMasterViewController singleton];
[shared resetAdview:self]
}
However in viewController.m i get the following errors:
https://dl.dropboxusercontent.com/u/63928888/Sk%C3%A6rmbillede%202014-09-15%20kl.%2000.17.02.png
Basically it doesn't recognize the returned "shared" err singleton (or whatever "shared" is) from GADMasterViewController.m
How do i "get" "shared" so my viewController doesn't produce these errors?
You are using shared, but you haven't defined it as a variable (local or otherwise).
So, change
shared = [GADMasterViewController singleton];
[shared resetAdview:self]
to either
GADMasterViewController *shared = [GADMasterViewController singleton];
[shared resetAdview:self];
or
[[GADMasterViewController singleton] resetAdview:self];
so that you are defining the variable, or so that you don't need a variable.

Resources