The error is as below.
The Handler is being deinitialized before Link has completed or been exited. Did you forget to strongly reference the Handler object returned by Plaid.create?
The functionality is working in the native app using the LinkKit framework. In order to use all the functions in objective C we have included a wrapper on top of it which is as below and when building a native app using the wrapper I am facing the above issue.
Wrapper Code.
// LinkKitSwiftwrapper.h
#import <Foundation/Foundation.h>
#import <LinkKit/LinkKit-Swift.h>
#import <UIKit/UIActivity.h>
NS_ASSUME_NONNULL_BEGIN
#interface LinkKitSwiftWrapper : NSObject
- (void)presentPlaidLinkUsingLinkToken: (NSString *)linkToken completionHandler:(void (^)(NSArray *array))callbackServer;
#property (strong, retain, nonatomic, readwrite) id<PLKHandler> linkHandler;
#end
NS_ASSUME_NONNULL_END
//LinkKitswiftWrapper.m
#import "LinkKitSwiftWrapper.h"
#import <LinkKit/LinkKit-Swift.h>
#import <UIKit/UIKit.h>
#import <LinkKit/LinkKit.h>
#implementation LinkKitSwiftWrapper
static void (^callBackObjectServer)(NSArray *);
- (void) presentPlaidLinkUsingLinkToken: (NSString *)linkToken completionHandler:(void (^)(NSArray *array))callbackServer
{
callBackObjectServer = callbackServer;
PLKLinkTokenConfiguration* linkConfiguration = [PLKLinkTokenConfiguration createWithToken:linkToken
onSuccess:^(PLKLinkSuccess *success) {
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *result = #[#"SUCCESS", success.publicToken];
callBackObjectServer(result);
});
}];
linkConfiguration.onExit = ^(PLKLinkExit * exit) {
if (exit.error) {
NSLog(#"exit with %#\n%#", exit.error, exit.metadata);
} else {
NSLog(#"exit with %#", exit.metadata);
}
};
NSError *createError = nil;
id<PLKHandler> handler = [PLKPlaid createWithLinkTokenConfiguration:linkConfiguration
error:&createError];
self.linkHandler = nil;
if (handler) {
[handler retain]
self.linkHandler = handler;
UIViewController *vc = [LinkKitSwiftWrapper topViewController] ;
[self.linkHandler openWithContextViewController:vc];
} else if (createError) {
NSLog(#"Unable to create PLKHandler due to: %#", createError);
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *result = #[#"ERROR", createError.description];
callBackObjectServer(result);
});
}else{
NSString *errorMessage = #"Unknown error occured while trying to create a Plaid link.";
NSLog(#"%#", errorMessage);
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *result = #[#"ERROR", errorMessage];
callBackObjectServer(result);
});
}
}
+ (UIViewController *)topViewController{
return [self topViewController:[[LinkKitSwiftWrapper keyWindow] rootViewController ]];
}
+ (UIViewController *)topViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil) {
return rootViewController ;
}
if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self topViewController:lastViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController ;
return [self topViewController:presentedViewController];
}
+(UIWindow*)keyWindow
{
UIWindow *foundWindow = nil;
NSArray *windows = [[UIApplication sharedApplication]windows];
for (UIWindow *window in windows) {
if (window.isKeyWindow) {
foundWindow = window;
break;
}
}
return foundWindow;
}
#end
//Native App code
#import "ViewController.h"
#import "PlaidWrapper/LinkKitSwiftWrapper.h"
#interface ViewController ()
#end
static void (^callBackObjectServer)(NSArray *);
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)ButtonTouchUpInside:(id)sender {
[ [LinkKitSwiftWrapper new] presentPlaidLinkUsingLinkToken:#"link-sandbox-0f671f81-c144-4160-8a64-0ce51e63dbcd" completionHandler:(void (^)(NSArray* array))callBackObjectServer];
}
#end
Please help.
Related
I am trying to create an app where I can send information from an apple watch to my ios Parent App. I have written the code for it but when I run the WatchConnectivity App, the information does not transfer between the apple watch and the parent ios app. This may be a problem with my code or it may be because for some reason the watch does not start with the app. I have to go to the simulator and click on the app to get it started. Is this why my code is not working?
InterfaceController.m
#import "InterfaceController.h"
#import <WatchConnectivity/WatchConnectivity.h>
#interface InterfaceController() <WCSessionDelegate>
#property (strong, nonatomic) WCSession *session;
#end
#implementation InterfaceController
-(instancetype)init {
self = [super init];
if (self) {
if ([WCSession isSupported]) {
self.session = [WCSession defaultSession];
self.session.delegate = self;
[self.session activateSession];
}
}
return self;
}
- (IBAction)catPressed {
[self sendText:#"cat"];
}
- (IBAction)dogPressed {
[self sendText:#"dog"];
}
- (IBAction)pandaPressed {
[self sendText:#"panda"];
}
- (IBAction)bunnyPressed {
[self sendText:#"bunny"];
}
-(void)sendText:(NSString *)text {
NSDictionary *applicationDict = #{#"emoji":text};
[self.session updateApplicationContext:applicationDict error:nil];
}
ViewController.m
#import "ViewController.h"
#import <WatchConnectivity/WatchConnectivity.h>
#interface ViewController () <WCSessionDelegate>
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
NSLog(#"HIIII");
}
}
- (void)session:(nonnull WCSession *)session didReceiveApplicationContext:(nonnull NSDictionary<NSString *,id> *)applicationContext {
NSString *text = [applicationContext objectForKey:#"text"];
dispatch_async(dispatch_get_main_queue(), ^{
[self.textLabel setText:[NSString stringWithFormat:#"Text: %#", text]];
});
}
It turns out that I needed to open the parent app on the iPhone first to start sharing information between the iPhone and Watch. Thanks to MSU_Bulldog for suggesting this idea.
I have created singleton class for AVAudioPlayer. I am able to call the methods in the class and everything works fine. When the song finishes,the method (void)audioPlayerDidFinishPlaying is called which in turn suppose to call the method ' processSuccessful' in my downloadPlay.m class. But, it is not calling the method 'processSuccessful'
My codes as follows
PlayerManager.h
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#protocol ProcessDataDelegate <NSObject>
#required
- (void) processSuccessful;
#end
#interface PlayerManager : NSObject<AVAudioPlayerDelegate,AVAudioSessionDelegate>
{
id <ProcessDataDelegate> delegate;
}
+ (PlayerManager *)sharedAudioPlayer;
#property (nonatomic,assign) id <ProcessDataDelegate>delegate;
#property (nonatomic, strong) AVAudioPlayer* player;
-(void)preparesong:(NSURL *)url;
-(void)stopsong;
-(void)pause;
-(void)playsong;
-(void)prepareToPlay;
-(BOOL)isPlaying;
-(BOOL)isPlayerExist;
#end
PlayerManager.m
#import "PlayerManager.h"
#interface PlayerManager()
#end
#implementation PlayerManager
#synthesize player;
#synthesize delegate;
static PlayerManager *sharedAudioPlayer = nil;
+ (PlayerManager *)sharedAudioPlayer {
static PlayerManager *sharedAudioPlayer = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedAudioPlayer = [[self alloc] init];
});
return sharedAudioPlayer ;
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags
{
if (flags & AVAudioSessionInterruptionOptionShouldResume)
{
[self.player play];
}
}
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
}
#pragma mark - AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[[self delegate] processSuccessful];
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
}
-(void)preparesong:(NSURL *)url
{
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
NSError *error;
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if(!self.player)
{
NSLog(#"Error creating player: %#", error);
}
self.player.delegate = self;
[self.player prepareToPlay];
}
-(BOOL)isPlayerExist
{
if (player)
return YES;
return NO;
}
-(BOOL)isPlaying
{
if (player && player.playing)
return YES;
return NO;
}
-(void)prepareToPlay
{
if (player)
[self.player prepareToPlay];
}
-(void)playsong
{
if (player)
[self.player play];
}
-(void)pause
{
if (player.playing)
[self.player pause];
}
-(void)stopsong
{
if (player)
[self.player stop];
}
#end
downloadPlay.h
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import "PlayerManager.h"
#interface downloadPlay: UIViewController <UITableViewDelegate,AVAudioPlayerDelegate,ProcessDataDelegate>
{
PlayerManager *protocolPlay;
}
#property (retain, nonatomic) IBOutlet UITableView *tblFiles;
......
- (void)startPlay:(id)sender;
........
#end
downloadPlay.m
import "downloadPlay.h"
#import "PlayerManager.h"
#interface downloadPlay ()
#end
#implementation downloadPlay
.....
- (void)processSuccessful
{
NSLog(#"This method suppose to be called from the method audioPlayerDidFinishPlaying - from PlayerManager");
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
protocolPlay = [[PlayerManager alloc]init];
[protocolPlay setDelegate:self];
}
- (void)startPlay
{
............
.........
NSURL *destinationURL = [self.docDirectoryURL URLByAppendingPathComponent:filename];
NSError* error = nil;
[[PlayerManager sharedAudioPlayer]stopsong];
[[PlayerManager sharedAudioPlayer ] preparesong:destinationURL ];
[[PlayerManager sharedAudioPlayer]playsong];
}
#end
In viewDidLoad method you are creating a different object by using
protocolPlay = [[PlayerManager alloc]init];
line and set the delegate of this object while you have to set the delegate of shared object.
Solution is:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[PlayerManager sharedAudioPlayer] setDelegate:self];
}
thanks in advance for any input. I have been trying to write a program that interprets a serial command into text change in a label. I can change the label text easily with an Action attached to a button, but for some reason all my attempts at changing the label text outside that button action result in nothing being changed.
// MasterViewController.m
//
// Created by Daniel Payne on 2/28/15.
// Copyright (c) 2015 Daniel Payne. All rights reserved.
//
#import "MasterViewController.h"
#interface MasterViewController ()
#property (strong) IBOutlet NSTextField *score;
#property (nonatomic, strong) ORSSerialPort *serialPort;
#property (nonatomic, strong) MasterViewController *masterView;
#end
static MasterViewController *serialPortHelper = nil;
void connectPort(void) {
ORSSerialPort *serialPort = [ORSSerialPort serialPortWithPath:#"/dev/tty.usbmodem1411"];
serialPortHelper = [[MasterViewController alloc] init];
serialPortHelper.serialPort = serialPort;
serialPort.delegate = serialPortHelper;
serialPort.baudRate = #9600;
NSLog(#"port open");
[serialPort open];
}
int main(int argc, const char * argv[]) {
connectPort();
return NSApplicationMain(argc, argv);
}
#implementation MasterViewController
- (IBAction)pushButton:(id)sender {
[self.score setStringValue:#"1"]; //works
}
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)awakeFromNib
{
NSLog(#"View controller instance with view: %#", self.view);
}
- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data
{
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if ([string rangeOfString:#"g"].location == NSNotFound) {
NSLog(#"no goal");
} else {
[self.score setStringValue:#"1"]; //does not work
NSLog(#"GOOOOAL");
}
}
- (void)serialPortWasRemovedFromSystem:(ORSSerialPort *)serialPort
{
self.serialPort = nil;
}
- (void)serialPort:(ORSSerialPort *)serialPort didEncounterError:(NSError *)error
{
NSLog(#"%s %# %#", __PRETTY_FUNCTION__, serialPort, error);
}
- (void)serialPortWasOpened:(ORSSerialPort *)serialPort
{
NSLog(#"Serial port %s was opened", [serialPort.name UTF8String]);
}
#end
The instance of MasterViewController that is listening to your serial port is not the same one that you are seeing on screen.
You create a new one and assign it to a static variable - this is not the same one you have loaded from a nib with all its outlets connected.
Log self in both methods to confirm.
My FetchVenuesView preceeds the VenuesIDController. VenuesIDController is the second tabbar item in a tabbarcontroller. FetchVenuesView is not part of the tabbar.
The first item in the tabbar is a tableview in which i can call a delegate without issue.
However when I try and call the delegate in VenuesIDController it always shows up in the log as null.
What do I do here? Do i connect the delegate in the storyboard? How?
I have a FetchVenuesViewController.h
#import "VenueTableViewController.h"
#import "VenueIDController.h"
#interface FetchVenuesViewController : UIViewController< VenueTableViewControllerDelegate, VenueIDControllerDelegate>{
NSDictionary* venueJSON;
NSDictionary* idJSON;
};
#property (strong) NSDictionary* idJSON;
- (void)VenueFetch;
- (void)IDFetch;
#end
In FetchVenuesViewController.m
#synthesize idJSON;
- (void)IDFetch {
//request some webservice
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
//save the response
if (data) {
id IDJSON = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (!IDJSON) {
//handle error
}
else {
//do something
}
} else {
// fetch failed
}
activityIndicator.hidden = NO;
}
-(NSDictionary *)getID{
[self IDfetch];
NSLog(#"json%#",idJSON);
return idJSON;
}
In VenueIDController.h
#protocol VenueIDControllerDelegate;
#interface VenueIDController : UIViewController{
}
#property (assign) id <VenueIDControllerDelegate> delegate;
-(IBAction)getIDData:(id)sender;
#end
#protocol VenueIDControllerDelegate <NSObject>
-(NSDictionary *)getID;
#end
and in VenueIDController.m
#interface VenueIDController (){
NSMutableArray* IDData;
UIImage* IDBarcode;
}
-(void) displayIDData:(NSDictionary*)data;
#end
#implementation VenueIDController
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
VenueIDController *vid = [[VenueIDController alloc] init];
vid.delegate = self;
NSLog(#"%#",vid);
}
return self;
}
-(void) displayIDData:(NSDictionary*)data{
[delegate getID];
NSDictionary* idJSON = data;
}
Your init on VenueIDController appears wrong. you're already in an init, so you don't need to create another one. instead you should have self.delegate = self. The vid object you are creating there isn't going to be retained.
I have created a singleton for my MusicBackground. And I receive a line code of imcomplete implementation of this line #implementation MyBgMusic. Can anyone tell me why ? Below is the code:
#import "MyBgMusic.h"
static MyBgMusic *sharedMyManager = nil;
#implementation MyBgMusic
#synthesize player,playBgMusic;
#pragma mark -
#pragma mark Singleton Methods
+ (MyBgMusic*)sharedInstance {
static MyBgMusic *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#if (!__has_feature(objc_arc))
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; //denotes an object that cannot be released
}
- (id)autorelease {
return self;
}
- (void)dealloc
{
[MyBgMusic release];
[playBgMusic release];
[player release];
[super dealloc];
}
#endif
#pragma mark -
#pragma mark Custom Methods
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"music" ofType:#"mp3"];
self.player=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
player.delegate = self;
[player play];
player.numberOfLoops = -1;
[super viewDidLoad];
}
#end
For the M file, below is the code:
#import <Foundation/Foundation.h>
#import <AVFoundation/AVAudioPlayer.h>
#interface MyBgMusic : UIViewController <AVAudioPlayerDelegate> {
AVAudioPlayer *player;
UIButton *playBgMusic;
}
#property (nonatomic, retain) IBOutlet AVAudioPlayer *player;
#property (nonatomic, retain) IBOutlet UIButton *playBgMusic;
+ (id)sharedManager;
-(IBAction) toggleMusic;
#end
And how do I reference to my toggle button: Below is the code :
- (IBAction)toggleMusic {
if ([self.player isPlaying] == YES) {
[self.player stop];
} else {
[self.player play];
}
self.playBgMusic.enabled = YES;
}
It means that your MyBgMusic class isn't doing everything it promised to do in its header file, which includes being a UIViewController and implementing the AVAudioPlayerDelegate protocol. I'm not familiar with exactly what the AVAudioPlayerDelegate is, but it's quite possible that your class doesn't implement all of the required methods.
Also, you're declaring methods +(id)sharedManager and -(IBAction)toggleMusic, but I don't see them anywhere in the implementation file. That would be a case of promising something in the header and not implementing it in the class.
It would help if you posted the actual error message.
That error means your #implementation section does not contain everything described in the #interface section.
I can see two problems.
First you need to place this code:
- (IBAction)toggleMusic {
...
}
Somewhere in between #implementation and #end.
And you also need to rename the line + (MyBgMusic*)sharedInstance to + (id)sharedManager.
EDIT:
To access the toggle music method elsewhere in your code, you would do:
[[MyBgMusic sharedManager] toggleMusic];
Your +(id)sharedManagerimplementation is called +(id)sharedInstance. Just guessing, but it seems they are supposed to do the same.