I have used VENTokenField github library for recipients email. Now the user enter the email and its separated by commas(,). Everything works fine. My question is how to clear the text when user press the submit button.
1) http://imgur.com/a/ObbaB
2) http://imgur.com/a/DCusg
#property (strong, nonatomic) NSMutableArray *inviteFriendNames;
self.inviteFriendNames = [[NSMutableArray alloc]init];
#pragma mark - VENTokenFieldDataSource
- (NSString *)tokenField:(VENTokenField *)tokenField titleForTokenAtIndex:(NSUInteger)index
{
if (self.inviteFriendNames.count > index) {
return self.inviteFriendNames[index];
}
return #"";
}
- (NSUInteger)numberOfTokensInTokenField:(VENTokenField *)tokenField
{
return self.inviteFriendNames.count;
}
#pragma mark - VENTokenFieldDelegate
- (void)tokenField:(VENTokenField *)tokenField didEnterText:(NSString *)text
{
if (![self.inviteFriendNames containsObject:text]) {
self.inviteFriendNames = (NSMutableArray*)[self.inviteFriendNames arrayByAddingObject:text];
[self chosenContactsHasChanged];
}
return;
}
- (void)tokenField:(VENTokenField *)tokenField didDeleteTokenAtIndex:(NSUInteger)index
{
if (self.inviteFriendNames.count > index) {
NSMutableArray *mutable = [self.inviteFriendNames mutableCopy];
[mutable removeObjectAtIndex:index];
self.inviteFriendNames = mutable;
[self chosenContactsHasChanged];
}
self.tokenField.placeholderText = NSLocalizedString(#"Email", nil);
[self chosenContactsHasChanged];
}
- (void)tokenField:(VENTokenField *)tokenField didChangeText:(NSString *)text
{
if ([text hasSuffix:#" "]) {
// Trim off the comma
text = [text stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#" "]];
[self tokenField:tokenField didEnterText:text];
} else {
return;
}
}
- (void)updateContactTokenFieldHeightConstraint
{
CGFloat heightOfTokens = self.tokenField.tokenScrollViewHeight + tokenFieldHeightPadding;
CGFloat newHeight = MAX(tokenFieldMinHeight, heightOfTokens);
self.tokenFieldHeight.constant = newHeight;
[self layoutIfNeeded];
}
- (void)chosenContactsHasChanged
{
[self.tokenField reloadData];
[self updateContactTokenFieldHeightConstraint];
}
pragma mark - UIButton Action
-(IBAction)inviteButtonAction:(id)sender{
[self endEditing:YES];
if (self.inviteFriendNames.count == 0) {
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:nil description:#"Please enter the mail id." completion:nil];
}else{
NSString *senderName = [NSString stringWithFormat:#"%#.%#",[APPDELEGATE.defaults valueForKey:#"firstName"],[APPDELEGATE.defaults valueForKey:#"lastName"]];
NSDictionary *sendData = #{#"name":senderName,#"emails":inviteFriendNames};
[self.inviteVC sendInviationAPI:INVITE_FRIENDS withParameter:sendData];
}
}
pragma mark - To invitation
-(void)sendInviationAPI:(NSString *)url withParameter:(NSDictionary *)parameters{
// To Check Network Available or Unavailable
if (![PCCommonUtility checkForNetwork]) {
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:[LocalizedStrings warning] description:[LocalizedStrings networkFailure] completion:nil];
}else{
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
// To Check Access token is null or empty
if ([APPDELEGATE.defaults valueForKey:CURRENT_TOKEN] && ![[APPDELEGATE.defaults valueForKey:CURRENT_TOKEN]isKindOfClass:[NSNull class]]) {
[PCAPIUtility getResponseFromAPI:url withParameters:parameters withSuccess:^(NSMutableArray *responseData, NSString *success) {
DLog(#"InviteSuccess:%#",responseData);
NSString *status = [NSString stringWithFormat:#"%#",[responseData valueForKey:#"status"]];
NSString *message = [NSString stringWithFormat:#"%#",[responseData valueForKey:#"message"]]
if ([status isEqualToString:#"1"]) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:nil description:message completion:^(BOOL confirm) {
if (!confirm) {
[self.inviteView.inviteFriendNames removeAllObjects];
[self.inviteView.tokenField reloadData];
}
}];
}else{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:nil description:message completion:nil];
}
} orFailure:^(NSError *error) {
DLog(#"InviteFailure:%#",error.description);
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:nil description:[LocalizedStrings networkFailure] completion:nil];
}];
}else{
NSLog(#"Access Token is Empty");
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
}
}
In ViewController class,
.h
#protocol DelegateMethod <NSObject>
-(void)removeArray;
#end
#interface ViewController : UIViewController
#property (nonatomic, strong) id <DelegateMethod> delegate;
#end
.m
-(void)sendInviationAPI:(NSString *)url withParameter:(NSDictionary *)parameters{
// To Check Network Available or Unavailable
if (![PCCommonUtility checkForNetwork]) {
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:[LocalizedStrings warning] description:[LocalizedStrings networkFailure] completion:nil];
}else{
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
// To Check Access token is null or empty
if ([APPDELEGATE.defaults valueForKey:CURRENT_TOKEN] && ![[APPDELEGATE.defaults valueForKey:CURRENT_TOKEN]isKindOfClass:[NSNull class]]) {
[PCAPIUtility getResponseFromAPI:url withParameters:parameters withSuccess:^(NSMutableArray *responseData, NSString *success) {
DLog(#"InviteSuccess:%#",responseData);
NSString *status = [NSString stringWithFormat:#"%#",[responseData valueForKey:#"status"]];
NSString *message = [NSString stringWithFormat:#"%#",[responseData valueForKey:#"message"]]
if ([status isEqualToString:#"1"]) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:nil description:message completion:^(BOOL confirm) {
if (!confirm) {
[self.delegate removeArray];
}
}];
}else{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:nil description:message completion:nil];
}
} orFailure:^(NSError *error) {
DLog(#"InviteFailure:%#",error.description);
[MBProgressHUD hideHUDForView:self.view animated:YES];
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:nil description:[LocalizedStrings networkFailure] completion:nil];
}];
}else{
NSLog(#"Access Token is Empty");
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
}
}
In View class
Invite Button Action
-(IBAction)inviteButtonAction:(id)sender{
[self endEditing:YES];
if (self.inviteFriendNames.count == 0) {
[[PCAlertUtility sharedInstance]showOkAlertWithTitle:nil description:#"Please enter the mail id." completion:nil];
}else{
NSString *senderName = [NSString stringWithFormat:#"%#.%#",[APPDELEGATE.defaults valueForKey:#"firstName"],[APPDELEGATE.defaults valueForKey:#"lastName"]];
NSDictionary *sendData = #{#"name":senderName,#"emails":inviteFriendNames};
[self.inviteVC setDelegate:self];
[self.inviteVC sendInviationAPI:INVITE_FRIENDS withParameter:sendData];
}
}
-(void)removeArray {
[self.inviteFriendNames removeAllObjects];
[self.tokenField reloadData];
}
Related
I am working on Twilio programmable voice SDK. I integrated it with the help of documentation and GitHub SDK. I can make a call from iOS to any number. It works fine.
The issue is on receiving a call on Twilio number. I did everything that was mentioned in Twilio installation documentation and GitHub SDK, but its not working.
here is the code:
#import PushKit;
#import CallKit;
#import TwilioVoice;
#import UserNotifications;
static NSString * kAccessToken = #"";
NSString * phoneNumber = #"";
NSString * newToken = #"";
static NSString *const kTwimlParamTo = #"to";
static NSInteger const kRegistrationTTLInDays = 365;
NSString * const kCachedDeviceToken = #"CachedDeviceToken";
NSString * const kCachedBindingTime = #"CachedBindingTime";
#interface RCTCallPackageModule () <TVONotificationDelegate, TVOCallDelegate, CXProviderDelegate, UITextFieldDelegate, AVAudioPlayerDelegate , PushKitEventDelegate, PKPushRegistryDelegate>
#property (nonatomic, weak) id<PushKitEventDelegate> pushKitEventDelegate;
#property (nonatomic, strong) void(^incomingPushCompletionCallback)(void);
#property (nonatomic, strong) void(^callKitCompletionCallback)(BOOL);
#property (nonatomic, strong) TVODefaultAudioDevice *audioDevice;
#property (nonatomic, strong) NSMutableDictionary *activeCallInvites;
#property (nonatomic, strong) NSMutableDictionary *activeCalls;
// activeCall represents the last connected call
#property (nonatomic, strong) TVOCall *activeCall;
#property (nonatomic, strong) CXProvider *callKitProvider;
#property (nonatomic, strong) CXCallController *callKitCallController;
#property (nonatomic, assign) BOOL userInitiatedDisconnect;
#property (nonatomic, assign) BOOL playCustomRingback;
#property (nonatomic, strong) AVAudioPlayer *ringtonePlayer;
#end
//#import <React/RCTLog.h>
#implementation RCTCallPackageModule
-(NSString *)fetchAccessToken {
NSString *accessToken = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://54.172.240.200:4000/accessToken"]
encoding:NSUTF8StringEncoding
error:nil];
return accessToken;
}
-(void) mainIntializerFunction {
self.pushKitEventDelegate = self;
self.callKitCallController = [[CXCallController alloc] init];
// fetchData();
/* Please note that the designated initializer `[CXProviderConfiguration initWithLocalizedName:]` has been deprecated on iOS 14. */
CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] initWithLocalizedName:#"Voice Quickstart"];
configuration.maximumCallGroups = 1;
configuration.maximumCallsPerCallGroup = 1;
self.callKitProvider = [[CXProvider alloc] initWithConfiguration:configuration];
[self.callKitProvider setDelegate:self queue:nil];
self.audioDevice = [TVODefaultAudioDevice audioDevice];
TwilioVoiceSDK.audioDevice = self.audioDevice;
self.activeCallInvites = [NSMutableDictionary dictionary];
self.activeCalls = [NSMutableDictionary dictionary];
self.playCustomRingback = NO;
[self MainFunctionToCall]; }
- (void)dealloc {
if (self.callKitProvider) {
[self.callKitProvider invalidate];
} }
-(void) MainFunctionToCall {
if (self.activeCall != nil) {
self.userInitiatedDisconnect = YES;
[self performEndCallActionWithUUID:self.activeCall.uuid];
} else {
NSUUID *uuid = [NSUUID UUID];
NSString *handle = #"Voice Bot";
[self checkRecordPermission:^(BOOL permissionGranted) {
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
// Enable or disable features based on authorization.
}];
// [[UIApplication sharedApplication] registerForRemoteNotifications];
[self performStartCallActionWithUUID:uuid handle:handle];
}];
}
}
// performEndCallActiveUUID
- (void)performEndCallActionWithUUID:(NSUUID *)uuid {
CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:uuid];
CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction];
[self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
if (error) {
NSLog(#"EndCallAction transaction request failed: %#", [error localizedDescription]);
}
else {
}
}];
}
// checkRecordPermission
- (void)checkRecordPermission:(void(^)(BOOL permissionGranted))completion {
AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
switch (permissionStatus) {
case AVAudioSessionRecordPermissionGranted:
// Record permission already granted.
completion(YES);
break;
case AVAudioSessionRecordPermissionDenied:
// Record permission denied.
completion(NO);
break;
case AVAudioSessionRecordPermissionUndetermined:
{
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
completion(granted);
}];
break;
}
default:
completion(NO);
break;
}
}
#pragma mark - CallKit Actions
- (void)performStartCallActionWithUUID:(NSUUID *)uuid handle:(NSString *)handle {
if (uuid == nil || handle == nil) {
return;
}
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:callHandle];
CXTransaction *transaction = [[CXTransaction alloc] initWithAction:startCallAction];
[self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
if (error) {
NSLog(#"StartCallAction transaction request failed: %#", [error localizedDescription]);
} else {
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = callHandle;
callUpdate.supportsDTMF = YES;
callUpdate.supportsHolding = YES;
callUpdate.supportsGrouping = NO;
callUpdate.supportsUngrouping = NO;
callUpdate.hasVideo = NO;
[self.callKitProvider reportCallWithUUID:uuid updated:callUpdate];
}
}];
}
#pragma mark - PushKitEventDelegate
- (void)credentialsUpdated:(PKPushCredentials *)credentials {
NSData *cachedDeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedDeviceToken];
if ([self registrationRequired] || ![cachedDeviceToken isEqualToData:credentials.token]) {
cachedDeviceToken = credentials.token;
[TwilioVoiceSDK registerWithAccessToken:kAccessToken
deviceToken:cachedDeviceToken
completion:^(NSError *error) {
if (error) {
} else {
// Save the device token after successfully registered.
[[NSUserDefaults standardUserDefaults] setObject:cachedDeviceToken forKey:kCachedDeviceToken];
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:kCachedBindingTime];
}
}];
}
}
- (BOOL)registrationRequired {
BOOL registrationRequired = YES;
NSDate *lastBindingCreated = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedBindingTime];
if (lastBindingCreated) {
NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
// Register upon half of the TTL
dayComponent.day = kRegistrationTTLInDays / 2;
NSDate *bindingExpirationDate = [[NSCalendar currentCalendar] dateByAddingComponents:dayComponent toDate:lastBindingCreated options:0];
NSDate *currentDate = [NSDate date];
if ([bindingExpirationDate compare:currentDate] == NSOrderedDescending) {
registrationRequired = NO;
}
}
return registrationRequired;
}
- (void)credentialsInvalidated {
NSData *cachedDeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedDeviceToken];
if ([cachedDeviceToken length] > 0) {
[TwilioVoiceSDK unregisterWithAccessToken:kAccessToken
deviceToken:cachedDeviceToken
completion:^(NSError *error) {
if (error) {
} else {
}
}];
}
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kCachedDeviceToken];
// Remove the cached binding as credentials are invalidated
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kCachedBindingTime];
}
-(void)incomingPushReceived:(PKPushPayload *)payload withCompletionHandler:(void (^)(void))completion {
// The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
}
if (completion) {
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
// Save for later when the notification is properly handled.
self.incomingPushCompletionCallback = completion;
} else {
completion();
}
}
}
- (void)incomingPushHandled {
if (self.incomingPushCompletionCallback) {
self.incomingPushCompletionCallback();
self.incomingPushCompletionCallback = nil;
}
}
#pragma mark - TVONotificationDelegate
- (void)callInviteReceived:(TVOCallInvite *)callInvite {
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:kCachedBindingTime];
if (callInvite.callerInfo.verified != nil && [callInvite.callerInfo.verified boolValue]) {
}
NSString *from = #"Voice Bot";
if (callInvite.from) {
from = [callInvite.from stringByReplacingOccurrencesOfString:#"client:" withString:#""];
}
// Always report to CallKit
[self reportIncomingCallFrom:from withUUID:callInvite.uuid];
self.activeCallInvites[[callInvite.uuid UUIDString]] = callInvite;
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
[self incomingPushHandled];
}
}
- (void)cancelledCallInviteReceived:(TVOCancelledCallInvite *)cancelledCallInvite error:(NSError *)error {
TVOCallInvite *callInvite;
for (NSString *uuid in self.activeCallInvites) {
TVOCallInvite *activeCallInvite = [self.activeCallInvites objectForKey:uuid];
if ([cancelledCallInvite.callSid isEqualToString:activeCallInvite.callSid]) {
callInvite = activeCallInvite;
break;
}
}
if (callInvite) {
[self performEndCallActionWithUUID:callInvite.uuid];
[self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
}
}
- (void)callDidStartRinging:(TVOCall *)call {
NSLog(#"callDidStartRinging:");
if (self.playCustomRingback) {
[self playRingback];
}
// [self.placeCallButton setTitle:#"Ringing" forState:UIControlStateNormal];
}
- (void)callDidConnect:(TVOCall *)call {
NSLog(#"callDidConnect:");
if (self.playCustomRingback) {
[self stopRingback];
}
[self sendEventWithName:#"onSessionConnect" body:#"Connected"];
self.callKitCompletionCallback(YES);
}
- (void)call:(TVOCall *)call isReconnectingWithError:(NSError *)error {
NSLog(#"Call is reconnecting");
}
- (void)callDidReconnect:(TVOCall *)call {
NSLog(#"Call reconnected");
}
- (void)call:(TVOCall *)call didFailToConnectWithError:(NSError *)error {
NSLog(#"Call failed to connect: %#", error);
self.callKitCompletionCallback(NO);
[self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonFailed];
[self sendEventWithName:#"onSessionConnect" body:#"Failure"];
[self callDisconnected:call];
}
- (void)call:(TVOCall *)call didDisconnectWithError:(NSError *)error {
if (error) {
NSLog(#"Call failed: %#", error);
} else {
NSLog(#"Call disconnected");
}
if (!self.userInitiatedDisconnect) {
CXCallEndedReason reason = CXCallEndedReasonRemoteEnded;
if (error) {
reason = CXCallEndedReasonFailed;
}
[self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:reason];
}
[self sendEventWithName:#"onSessionConnect" body:#"Disconnected"];
[self callDisconnected:call];
}
- (void)callDisconnected:(TVOCall *)call {
if ([call isEqual:self.activeCall]) {
self.activeCall = nil;
}
[self.activeCalls removeObjectForKey:call.uuid.UUIDString];
self.userInitiatedDisconnect = NO;
if (self.playCustomRingback) {
[self stopRingback];
}
}
- (void)call:(TVOCall *)call
didReceiveQualityWarnings:(NSSet<NSNumber *> *)currentWarnings
previousWarnings:(NSSet<NSNumber *> *)previousWarnings {
/**
* currentWarnings: existing quality warnings that have not been cleared yet
* previousWarnings: last set of warnings prior to receiving this callback
*
* Example:
* - currentWarnings: { A, B }
* - previousWarnings: { B, C }
* - intersection: { B }
*
* Newly raised warnings = currentWarnings - intersection = { A }
* Newly cleared warnings = previousWarnings - intersection = { C }
*/
NSMutableSet *warningIntersetction = [currentWarnings mutableCopy];
[warningIntersetction intersectSet:previousWarnings];
NSMutableSet *newWarnings = [currentWarnings mutableCopy];
[newWarnings minusSet:warningIntersetction];
if ([newWarnings count] > 0) {
[self qualityWarningUpdatePopup:newWarnings isCleared:NO];
}
NSMutableSet *clearedWarnings = [previousWarnings mutableCopy];
[clearedWarnings minusSet:warningIntersetction];
if ([clearedWarnings count] > 0) {
[self qualityWarningUpdatePopup:clearedWarnings isCleared:YES];
}
}
- (void)qualityWarningUpdatePopup:(NSSet *)warnings isCleared:(BOOL)cleared {
NSString *popupMessage = (cleared)? #"Warnings cleared:" : #"Warnings detected:";
for (NSNumber *warning in warnings) {
NSString *warningName = [self warningString:[warning unsignedIntValue]];
popupMessage = [popupMessage stringByAppendingString:[NSString stringWithFormat:#" %#", warningName]];
}
[UIView animateWithDuration:1.0f
animations:^{
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:1.0 animations:^{
} completion:^(BOOL finished) {
}];
});
}];
}
- (NSString *)warningString:(TVOCallQualityWarning)qualityWarning {
switch (qualityWarning) {
case TVOCallQualityWarningHighRtt:
return #"high-rtt";
break;
case TVOCallQualityWarningHighJitter:
return #"high-jitter";
break;
case TVOCallQualityWarningHighPacketsLostFraction:
return #"high-packets-lost-fraction";
break;
case TVOCallQualityWarningLowMos:
return #"low-mos";
break;
case TVOCallQualityWarningConstantAudioInputLevel:
return #"constant-audio-input-level";
break;
default:
return #"Unknown warning";
break;
}
}
-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
NSLog(#"pushRegistry:didReceiveIncomingPushWithPayload:forType:");
if ([type isEqualToString:PKPushTypeVoIP]) {
// The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
NSLog(#"This is not a valid Twilio Voice notification.");
}
else
{
}
}
}
/**
* This delegate method is available on iOS 11 and above. Call the completion handler once the
* notification payload is passed to the `TwilioVoice.handleNotification()` method.
*/
- (void)pushRegistry:(PKPushRegistry *)registry
didReceiveIncomingPushWithPayload:(PKPushPayload *)payload
forType:(PKPushType)type
withCompletionHandler:(void (^)(void))completion {
NSLog(#"pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:");
// Save for later when the notification is properly handled.
self.incomingPushCompletionCallback = completion;
if ([type isEqualToString:PKPushTypeVoIP]) {
// The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
NSLog(#"This is not a valid Twilio Voice notification.");
}
}
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
// Save for later when the notification is properly handled.
self.incomingPushCompletionCallback = completion;
} else {
/**
* The Voice SDK processes the call notification and returns the call invite synchronously. Report the incoming call to
* CallKit and fulfill the completion before exiting this callback method.
*/
completion();
}
}
#pragma mark - AVAudioSession
- (void)toggleAudioRoute:(BOOL)toSpeaker {
// The mode set by the Voice SDK is "VoiceChat" so the default audio route is the built-in receiver. Use port override to switch the route.
self.audioDevice.block = ^ {
// We will execute `kDefaultAVAudioSessionConfigurationBlock` first.
kTVODefaultAVAudioSessionConfigurationBlock();
// Overwrite the audio route
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
if (toSpeaker) {
if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error]) {
NSLog(#"Unable to reroute audio: %#", [error localizedDescription]);
}
} else {
if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&error]) {
NSLog(#"Unable to reroute audio: %#", [error localizedDescription]);
}
}
};
self.audioDevice.block();
}
#pragma mark - CXProviderDelegate
- (void)providerDidReset:(CXProvider *)provider {
NSLog(#"providerDidReset:");
self.audioDevice.enabled = NO;
}
- (void)providerDidBegin:(CXProvider *)provider {
NSLog(#"providerDidBegin:");
}
- (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession {
NSLog(#"provider:didActivateAudioSession:");
self.audioDevice.enabled = YES;
}
- (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSession *)audioSession {
NSLog(#"provider:didDeactivateAudioSession:");
self.audioDevice.enabled = NO;
}
- (void)provider:(CXProvider *)provider timedOutPerformingAction:(CXAction *)action {
NSLog(#"provider:timedOutPerformingAction:");
}
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
NSLog(#"provider:performStartCallAction:");
[self.callKitProvider reportOutgoingCallWithUUID:action.callUUID startedConnectingAtDate:[NSDate date]];
__weak typeof(self) weakSelf = self;
[self performVoiceCallWithUUID:action.callUUID client:nil completion:^(BOOL success) {
__strong typeof(self) strongSelf = weakSelf;
if (success) {
NSLog(#"performVoiceCallWithUUID successful");
[strongSelf.callKitProvider reportOutgoingCallWithUUID:action.callUUID connectedAtDate:[NSDate date]];
} else {
NSLog(#"performVoiceCallWithUUID failed");
}
[action fulfill];
}];
}
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
NSLog(#"provider:performAnswerCallAction:");
[self performAnswerVoiceCallWithUUID:action.callUUID completion:^(BOOL success) {
if (success) {
NSLog(#"performAnswerVoiceCallWithUUID successful");
} else {
NSLog(#"performAnswerVoiceCallWithUUID failed");
}
}];
[action fulfill];
}
- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {
NSLog(#"provider:performEndCallAction:");
TVOCallInvite *callInvite = self.activeCallInvites[action.callUUID.UUIDString];
TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
if (callInvite) {
[callInvite reject];
[self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
} else if (call) {
[call disconnect];
} else {
NSLog(#"Unknown UUID to perform end-call action with");
}
[action fulfill];
}
- (void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action {
TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
if (call) {
[call setOnHold:action.isOnHold];
[action fulfill];
} else {
[action fail];
}
}
- (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCallAction *)action {
TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
if (call) {
[call setMuted:action.isMuted];
[action fulfill];
} else {
[action fail];
}
}
#pragma mark - CallKit Actions
- (void)reportIncomingCallFrom:(NSString *) from withUUID:(NSUUID *)uuid {
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:from];
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = callHandle;
callUpdate.supportsDTMF = YES;
callUpdate.supportsHolding = YES;
callUpdate.supportsGrouping = NO;
callUpdate.supportsUngrouping = NO;
callUpdate.hasVideo = NO;
[self.callKitProvider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError *error) {
if (!error) {
}
else {
}
}];
}
- (void)performVoiceCallWithUUID:(NSUUID *)uuid
client:(NSString *)client
completion:(void(^)(BOOL success))completionHandler {
__weak typeof(self) weakSelf = self;
TVOConnectOptions *connectOptions = [TVOConnectOptions optionsWithAccessToken:kAccessToken block:^(TVOConnectOptionsBuilder *builder) {
__strong typeof(self) strongSelf = weakSelf;
builder.params = #{kTwimlParamTo:phoneNumber};
builder.uuid = uuid;
}];
TVOCall *call = [TwilioVoiceSDK connectWithOptions:connectOptions delegate:self];
if (call) {
self.activeCall = call;
self.activeCalls[call.uuid.UUIDString] = call;
}
self.callKitCompletionCallback = completionHandler;
}
- (void)performAnswerVoiceCallWithUUID:(NSUUID *)uuid
completion:(void(^)(BOOL success))completionHandler {
TVOCallInvite *callInvite = self.activeCallInvites[uuid.UUIDString];
NSAssert(callInvite, #"No CallInvite matches the UUID");
TVOAcceptOptions *acceptOptions = [TVOAcceptOptions optionsWithCallInvite:callInvite block:^(TVOAcceptOptionsBuilder *builder) {
builder.uuid = callInvite.uuid;
}];
TVOCall *call = [callInvite acceptWithOptions:acceptOptions delegate:self];
if (!call) {
completionHandler(NO);
} else {
self.callKitCompletionCallback = completionHandler;
self.activeCall = call;
self.activeCalls[call.uuid.UUIDString] = call;
}
[self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
[self incomingPushHandled];
}
}
#pragma mark - Ringtone
- (void)playRingback {
NSString *ringtonePath = [[NSBundle mainBundle] pathForResource:#"ringtone" ofType:#"wav"];
if ([ringtonePath length] <= 0) {
return;
}
NSError *error;
self.ringtonePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:ringtonePath] error:&error];
if (error != nil) {
} else {
self.ringtonePlayer.delegate = self;
self.ringtonePlayer.numberOfLoops = -1;
self.ringtonePlayer.volume = 1.0f;
[self.ringtonePlayer play];
}
}
- (void)stopRingback {
if (!self.ringtonePlayer.isPlaying) {
return;
}
[self.ringtonePlayer stop];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
if (flag) {
NSLog(#"Audio player finished playing successfully");
} else {
NSLog(#"Audio player finished playing with some error");
}
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
NSLog(#"Decode error occurred: %#", error);
}
#end
I'm developing a location-based mobile application. My app updates the server with user's current location even when the app is killed/terminated. My app was working fine before integrating google place picker API. After installing google place picker API for ios, my app updates the location for about 5 to 10 minutes only and then it stops updating location.
//My code :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize Location Manager
sLocationManager;
//the system automatically relaunches the application into the background if a new event arrives.
// The options dictionary passed to the application:didFinishLaunchingWithOptions: method of your application delegate contains the key UIApplicationLaunchOptionsLocationKey to indicate that your application was launched because of a location event
if (launchOptions[UIApplicationLaunchOptionsLocationKey]) {
NSLog(#"---UIApplicationLaunchOptionsLocationKey");
sLocationManager.locationUpdatedInBackground = ^(NSArray<CLLocation *> *locations) {
NSLog(#"---setLocationUpdatedInBackground");
[sLocationModule saveNewLocations];
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:300];
notification.alertBody = #"Your location was changed. Please run application for tracking your geoposition with best accuracy.";
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
[sLocationManager startMonitoringSignificantLocationChanges];
};
}else {
sLocationManager.locationUpdatedInForeground = ^(NSArray<CLLocation *> *locations) {
NSLog(#"---setLocationUpdatedInForeground");
[sLocationModule saveNewLocations];
};
sLocationManager.locationUpdatedInBackground = ^(NSArray<CLLocation *> *locations) {
NSLog(#"---setLocationUpdatedInBackground");
[sLocationModule saveNewLocations];
[sLocationManager startDeferredLocationUpdates];
};
// Notifications
[application cancelAllLocalNotifications];
}
return YES;
//location manager
#pragma mark - CLLocationManager Delegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)aLocations {
NSString *infoMessage = [NSString stringWithFormat:#"%#", aLocations];
NSLog(#"%#", infoMessage);
locations = [#[] mutableCopy];
NSLog(#"locations=%#",locations);
[aLocations enumerateObjectsUsingBlock:^(CLLocation * _Nonnull location, NSUInteger idx, BOOL * _Nonnull stop) {
// Check for location errors (speed -1.00 mps / course -1.00)
// if (location.speed >= 0 &&
// location.course >= 0) {
if (locations.count > 0) {
if ([location.timestamp timeIntervalSinceDate:[locations lastObject].timestamp] > dLocationManagerDistanceFilter ||
[location distanceFromLocation:[locations lastObject]] > dLocationUpdatesUntilTimeout) {
[locations addObject:location];
NSLog(#"location.timestamp>dLocationManagerDistanceFilter**** locations=%#",locations);
}
} else
[locations addObject:location];
NSLog(#"else***locations=%#",locations);
//save location in userdefaults
NSString *latitudeValue = [NSString stringWithFormat:#"%f", location.coordinate.latitude];
NSString *longitudeValue = [NSString stringWithFormat:#"%f",location.coordinate.longitude];
NSLog(#"new location is recieved Lat : %# Long : %#",latitudeValue,longitudeValue);
[User setUserLat:latitudeValue];
[User setUserLon:longitudeValue];
// }
}];
if ([self isInBackground]) {
NSLog(#"app is in background***");
if (self.locationUpdatedInBackground) {
backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];
}];
self.locationUpdatedInBackground(locations);
[delegate locationManagerDidUpdateLocationInBackground];
[[NSNotificationCenter defaultCenter] postNotificationName:nLocationManagerDidUpdateLocationInBackgroundNotification
object:nil
userInfo:nil];
[self endBackgroundTask];
}
} else {
if (self.locationUpdatedInForeground) {
if([[NSUserDefaults standardUserDefaults]objectForKey:#"userIdKey"]){
self.locationUpdatedInForeground(locations);
[delegate locationManagerDidUpdateLocationInForeground];
[[NSNotificationCenter defaultCenter] postNotificationName:nLocationManagerDidUpdateLocationInForegroundNotification
object:nil
userInfo:nil];
}
}
}
}
code for adding place picker
#pragma mark - Place picker delegate methods
- (IBAction)pickPlace:(id)sender {
//dismiss the keyboard
[tfLocation resignFirstResponder];
CLLocationCoordinate2D center = CLLocationCoordinate2DMake(locationManager.location.coordinate.latitude, locationManager.location.coordinate.longitude);
CLLocationCoordinate2D northEast = CLLocationCoordinate2DMake(center.latitude + 0.001, center.longitude + 0.001);
CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(center.latitude - 0.001, center.longitude - 0.001);
GMSCoordinateBounds *viewport = [[GMSCoordinateBounds alloc] initWithCoordinate:northEast
coordinate:southWest];
GMSPlacePickerConfig *config = [[GMSPlacePickerConfig alloc] initWithViewport:viewport];
_placePicker = [[GMSPlacePicker alloc] initWithConfig:config];
[_placePicker pickPlaceWithCallback:^(GMSPlace *place, NSError *error) {
if (error != nil) {
NSLog(#"Pick Place error %#", [error localizedDescription]);
return;
}
if (place != nil) {
NSLog(#"Place name %#", place.name);
NSLog(#"Place address %#", place.formattedAddress);
NSLog(#"Place attributions %#", place.attributions.string);
// NSString *latitudeValue = [NSString stringWithFormat:#"%f", locationManager.location.coordinate.latitude];
// NSString *longitudeValue = [NSString stringWithFormat:#"%f",locationManager.location.coordinate.longitude];
tfLocation.text=place.formattedAddress;
locationPoint=[NSString stringWithFormat:#"%f,%f", place.coordinate.latitude,place.coordinate.longitude];
} else {
NSLog(#"No place selected");
}
}];
}
// Add a UIButton in Interface Builder, and connect the action to this function.
- (IBAction)getCurrentPlace:(UIButton *)sender {
[_placesClient currentPlaceWithCallback:^(GMSPlaceLikelihoodList *placeLikelihoodList, NSError *error){
if (error != nil) {
NSLog(#"Pick Place error %#", [error localizedDescription]);
return;
}
self.nameLabel.text = #"No current place";
self.addressLabel.text = #"";
if (placeLikelihoodList != nil) {
GMSPlace *place = [[[placeLikelihoodList likelihoods] firstObject] place];
if (place != nil) {
self.nameLabel.text = place.name;
self.addressLabel.text = [[place.formattedAddress componentsSeparatedByString:#", "]
componentsJoinedByString:#"\n"];
}
}
}];
}
// Present the autocomplete view controller when the button is pressed.
- (IBAction)onLaunchClicked:(id)sender {
GMSAutocompleteViewController *acController = [[GMSAutocompleteViewController alloc] init];
acController.delegate = self;
[self presentViewController:acController animated:YES completion:nil];
}
// Handle the user's selection.
- (void)viewController:(GMSAutocompleteViewController *)viewController
didAutocompleteWithPlace:(GMSPlace *)place {
[self dismissViewControllerAnimated:YES completion:nil];
// Do something with the selected place.
NSLog(#"Place name %#", place.name);
NSLog(#"Place address %#", place.formattedAddress);
NSLog(#"Place attributions %#", place.attributions.string);
}
- (void)viewController:(GMSAutocompleteViewController *)viewController
didFailAutocompleteWithError:(NSError *)error {
[self dismissViewControllerAnimated:YES completion:nil];
// TODO: handle the error.
NSLog(#"Error: %#", [error description]);
}
// User canceled the operation.
- (void)wasCancelled:(GMSAutocompleteViewController *)viewController {
[self dismissViewControllerAnimated:YES completion:nil];
}
// Turn the network activity indicator on and off again.
- (void)didRequestAutocompletePredictions:(GMSAutocompleteViewController *)viewController {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}
- (void)didUpdateAutocompletePredictions:(GMSAutocompleteViewController *)viewController {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
Can anyone tell, what am I doing wrong here? What is the best to use place picker without affecting background location updates or is there any other way to pick place/location just like google place picker in ios?
Please help me out or tell me the other solution.
thank you!
On Implementing the foursquare api for login through foursquare. How do i get user data after login on safari. I get access token after return back to my app. I implement Foursquare API v2 for iOS using pod.
implement Four Square API key
write below code in Appdelegate.m File
//Integarate FourSquare
NSString *yourClientId = #"WSTKVX3HJDWNZHEGZT3PLJQZNP5DUBDIEEUJZC2WVTOXRVWN";
NSString *yourClientSecret = #"R1BR2I5DCSLYNDO1BIGIFNJFMO5SOLOL12WNAPO3FZVIF0Z2";
NSString *yourCallbackURl = #"http://www.TestApp.com/Tapp"; //yourapp://foursquare
[UXRFourSquareNetworkingEngine registerFourSquareEngineWithClientId:yourClientId andSecret:yourClientSecret andCallBackURL:yourCallbackURl];
self.fourSquareEngine = [UXRFourSquareNetworkingEngine sharedInstance];
Write Below method to get category from FolurSquare Api
-(void)fetchCategoryDataFromFourSquare
{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"";
[ApplicationDelegate.fourSquareEngine getCategoriesWithCompletionBlock:^(NSDictionary *dictCategoryInfo)
{
NSArray *arr = [dictCategoryInfo objectForKey:#"categories"];
mutArrCategoryData = [arr mutableCopy];
[MBProgressHUD hideHUDForView:self.view animated:YES];
[tblView reloadData];
} failureBlock:^(NSError *error)
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
NSLog(#"%#",error);
}];
}
Get Vanue Detail
[ApplicationDelegate.fourSquareEngine getVeuneDetail:_strVenueID withCompletionBlock:^(NSDictionary *dictNearbyData)
{
[self setDetailsWithInfo:[dictNearbyData objectForKey:#"venue"]];
[MBProgressHUD hideHUDForView:self.view animated:YES];
} failureBlock:^(NSError *error)
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
// NSLog(#"%#",error);
}];
Get Near by Location
NSString *url = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/photo?photoreference=%#&key=%#&sensor=false&maxwidth=320", photoRef, kGOOGLE_API_KEY];
[ApplicationDelegate.fourSquareEngine getNearLocation:loc withCompletionBlock:^(NSDictionary *dictNearbyData)
{
NSArray *arr = [dictNearbyData objectForKey:#"venues"];
if([arr count]!=0)
{
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"location.distance" ascending:YES];
NSMutableArray *tempArray = [arr mutableCopy];
[tempArray sortUsingDescriptors:#[sort]];
for(int i=0;i<[tempArray count];i++)
{
if([[[tempArray objectAtIndex:i] valueForKeyPath:#"location.distance"] intValue] < 804.5)
{
[mutArrNearByLocationData addObject:[tempArray objectAtIndex:i]];
}
}
// ----- Add predicate for check whose distance is less than 0.5 miles (804.5 meters)
// NSPredicate *DistancePredicate = [NSPredicate predicateWithFormat:#"distance <= 804.5"];
// NSArray *LoyaltyfilteredAry = [mutArrNearByLocationData filteredArrayUsingPredicate:DistancePredicate];
[MBProgressHUD hideHUDForView:self.view animated:YES];
[tblView reloadData];
}
else
{
showAlertView(#"No locations within 0.5 miles");
}
//[self showNearByPlaceOnMap:mutArrNearByLocationData];
} failureBlock:^(NSError *error)
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
NSLog(#"%#",error);
}];
I have integrated the latest evernote ios sdk from github, That support both regular user and also business user. I can able to list the note book and notes without any issues but when i try to download the note attachment its giving me the following error.
Error Domain=ENErrorDomain Code=1 "Missing result: getResourceData
failed: unknown result" UserInfo=0x10dcb15f0
{NSLocalizedDescription=Missing result: getResourceData failed:
unknown result} Skipping field: due to type mismatch
(received:11)
Here the code i have used to download the note attachment.
+ (void)downloadDataWithNoteBook:(ENNotebook *)notebook fromNoteResource:(EDAMResource *)resource
onCompletion:(ZIdResultBlock)block {
ENPreferencesStore *preference = [ENSession sharedSession].preferences;
ENNoteStoreClient *noteStoreClient = [preference objectForKey:[SEEvernoteHelper getTitleForNoteAttachments:resource]];
NSString *str = resource.guid;
NSLog(#"GUID = [%#]", resource.guid);
[noteStoreClient getResourceDataWithGuid:resource.guid success:^(NSData *data) {
if(block) {
block(data, nil);
}
}
failure:^(NSError *error) {
if (block) {
block(nil, error);
}}];
}
Finally i was able to do it!!!
Here is my code:
//select the file that you want to download from evernote
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
isParentNoteBooks = false;
if ([SEEvernoteHelper isNoteBookInstance:[self.datasourceObjects objectAtIndex:indexPath.row]]) {
self.selectedNoteBook = [self.datasourceObjects objectAtIndex:indexPath.row];
[self fetchAttachmentsForNoteBook:[self.datasourceObjects objectAtIndex:indexPath.row]];
// set here if the notebook is business or not.
if ([SEEvernoteHelper isBussinessNoteBook:self.selectedNoteBook]) {
isCurrentNoteBookBusiness = YES;
}
else{
isCurrentNoteBookBusiness = NO;
}
} else {
self.selectedFileNameToDownload = [SEEvernoteHelper getTitleForNoteAttachments:[self.datasourceObjects objectAtIndex:indexPath.row]];
self.selectedResource = [self.datasourceObjects objectAtIndex:indexPath.row];
[self downloadNoteResultObject:self.selectedResource];
}
}
//start the download process
- (void)downloadNoteResultObject:(id)resultObject {
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = kStrDownloadingLabel;
HUD.detailsLabelText = self.selectedFileNameToDownload;
__weak __typeof__(self) weakSelf = self;
[SEEvernoteHelper downloadDataWithNoteBook:self.selectedNoteBook fromNoteResource:resultObject onCompletion:^(id result, NSError *error) {
if (result) {
[NSThread syncOnMain:^{
//save the downloaded data....
[HUD hide:YES];
}];
} else {
[HUD hide:YES];
[NSThread syncOnMain:^{
//error
}];
}
}];
}
//get the download file
+ (void)downloadDataWithNoteBook:(ENNotebook *)notebook fromNoteResource:(EDAMResource *)resource onCompletion:(ZIdResultBlock)block {
ENNoteStoreClient *noteStoreClient = [[ENSession sharedSession] noteStoreForNotebook:notebook];
[noteStoreClient getResourceWithGuid:resource.guid withData:YES withRecognition:NO withAttributes:YES withAlternateDate:NO success:^(EDAMResource *resource) {
if(block) {
block(resource.data.body, nil);
}
} failure:^(NSError *error) {
NSLog(#"Error : %#",error);
if (block) {
block(nil, error);
}
}];
}
I have an iPhone app connects to a server using OAuth. On success, it fetches the a user from the server. Again, upon success, it adds an item to the array of objects that populates the table view. Here is the code that does this:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
if (editing) {
[super setEditing:YES animated:YES];
self.backButton = self.navigationItem.leftBarButtonItem;
UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(signInWithCatapult)];
self.navigationItem.leftBarButtonItem = leftButton;
} else {
[super setEditing:NO animated:YES];
self.navigationItem.leftBarButtonItem = self.backButton;
}
}
- (void)signInWithCatapult
{
[self signOut];
GTMOAuth2Authentication *auth = [self catapultAuthenticaiton];
NSURL *authURL = [NSURL URLWithString:#"https://oauth.lvh.me:3000/oauth/authorize"];
GTMOAuth2ViewControllerTouch *viewController;
viewController = [[GTMOAuth2ViewControllerTouch alloc] initWithAuthentication:auth
authorizationURL:authURL
keychainItemName:kCatapultKeychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
[[self navigationController] pushViewController:viewController animated:YES];
}
- (GTMOAuth2Authentication *)catapultAuthenticaiton
{
NSURL *tokenURL = [NSURL URLWithString:kDoorkeeperTokenURL];
NSString *redirectURI = #"https://catapultcentral.com/iOSClientCallback";
GTMOAuth2Authentication *auth;
auth = [GTMOAuth2Authentication authenticationWithServiceProvider:#"Catapult Central"
tokenURL:tokenURL
redirectURI:redirectURI
clientID:kDoorkeeperClientID
clientSecret:kDoorkeeperClientSecret];
return auth;
}
- (void)signOut
{
}
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error
{
if (error != nil) {
#if DEBUG
NSLog(#"ERROR: %#", error);
#endif
} else {
NSURL *url = [NSURL URLWithString:#"https://api.lvh.me:3000/api/users/me"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
GTMHTTPFetcher *fetcher = [GTMHTTPFetcher fetcherWithRequest:request];
[fetcher setAuthorizer:auth];
[fetcher beginFetchWithDelegate:self didFinishSelector:#selector(currentUserFetcher:finishedWithData:error:)];
}
}
- (void)currentUserFetcher:(GTMHTTPFetcher *)fetcher
finishedWithData:(NSData *)data
error:(NSError *)error
{
if (error != nil) {
#if DEBUG
NSLog(#"ERROR: %#", error);
#endif
} else {
NSLog(#"Before: %#", self.accounts);
[self.tableView beginUpdates];
[self.accounts addObject:#"Success!!!"];
[self.tableView endUpdates];
// [self.tableView reloadData];
NSLog(#"After %#", self.accounts);
}
}
It's in the currentUserFetcher:finishedWithData:error: method that I add the object to the self.accounts mutable array. Now if I use this code it doesn't work:
[self.tableView beginUpdates];
[self.accounts addObject:#"Success!!!"];
[self.tableView endUpdates];
It fails at the line [self.tableView endUpdates]; with the following error message:
2013-03-28 08:56:21.040 Catapult for iOS[55012:c07] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:1054
And on the endUpdates line, XCode is complaining saying Thread 1: breakpoint 1.3. Now, if I use this code, it works normally:
[self.accounts addObject:#"Success!!!"];
[self.tableView reloadData];
Now I suspect that it is failing because I add an object to the self.accounts instance variable but I don't actually add the cell. So my question is: How do I add a cell to the tableView from the currentUserFetcher:finishedWithData:error: method?
If you just override this method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
Calling [UITableView reloadData] should just work itself out. The UITableViewController will just ask the amount of data (cells) that are there (using "tableView:numberOfRowsInSection:") and is requesting the Cell for every indexPath using the first mentioned method.