after tweaking the code i reduced 3 errors into 2 but I'm still unsure how to resolve the final errors.
-No known class method for selector 'getExportedModuleForName:'
-No known class method for selector 'getExportedModule:'
#import <UMReactNativeAdapter/UMReactNativeEventEmitter.h>
#import <UMCore/UMEventEmitter.h>
#import <UMCore/UMExportedModule.h>
#import <UMCore/UMModuleRegistry.h>
#interface UMReactNativeEventEmitter ()
#property (nonatomic, assign) int listenersCount;
#property (nonatomic, weak) UMModuleRegistry *ModuleRegistry;
#property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *modulesListenersCounts;
#end
#implementation UMReactNativeEventEmitter
- (instancetype)init
{
if (self = [super init]) {
_listenersCount = 0;
_modulesListenersCounts = [NSMutableDictionary dictionary];
}
return self;
}
UM_REGISTER_MODULE();
+ (NSString *)moduleName
{
return #"UMReactNativeEventEmitter";
}
+ (const NSArray<Protocol *> *)exportedInterfaces
{
return #[#protocol(UMEventEmitterService)];
}
- (NSArray<NSString *> *)supportedEvents
{
NSMutableSet<NSString *> *eventsAccumulator = [NSMutableSet set];
for (UMExportedModule *exportedModule in [UMModuleRegistry getAllExportedModules]) {
if ([exportedModule conformsToProtocol:#protocol(UMEventEmitter)]) {
id<UMEventEmitter> eventEmitter = (id<UMEventEmitter>)exportedModule;
[eventsAccumulator addObjectsFromArray:[eventEmitter supportedEvents]];
}
}
return [eventsAccumulator allObjects];
}
RCT_EXPORT_METHOD(addProxiedListener:(NSString *)moduleName eventName:(NSString *)eventName)
{
[self addListener:eventName];
// Validate module
UMExportedModule *module = [UMModuleRegistry getExportedModuleForName:moduleName];
if (RCT_DEBUG && module == nil) {
UMLogError(#"Module for name `%#` has not been found.", moduleName);
return;
} else if (RCT_DEBUG && ![module conformsToProtocol:#protocol(UMEventEmitter)]) {
UMLogError(#"Module `%#` is not an UMEventEmitter, thus it cannot be subscribed to.", moduleName);
return;
}
// Validate eventEmitter
id<UMEventEmitter> eventEmitter = (id<UMEventEmitter>)module;
if (RCT_DEBUG && ![[eventEmitter supportedEvents] containsObject:eventName]) {
UMLogError(#"`%#` is not a supported event type for %#. Supported events are: `%#`",
eventName, moduleName, [[eventEmitter supportedEvents] componentsJoinedByString:#"`, `"]);
}
// Global observing state
_listenersCount += 1;
if (_listenersCount == 1) {
[self startObserving];
}
// Per-module observing state
int newModuleListenersCount = [self moduleListenersCountFor:moduleName] + 1;
if (newModuleListenersCount == 1) {
[eventEmitter startObserving];
}
_modulesListenersCounts[moduleName] = [NSNumber numberWithInt:newModuleListenersCount];
}
RCT_EXPORT_METHOD(removeProxiedListeners:(NSString *)moduleName count:(double)count)
{
[self removeListeners:count];
// Validate module
UMExportedModule *module = [UMModuleRegistry getExportedModuleForName:moduleName];
if (RCT_DEBUG && module == nil) {
UMLogError(#"Module for name `%#` has not been found.", moduleName);
return;
} else if (RCT_DEBUG && ![module conformsToProtocol:#protocol(UMEventEmitter)]) {
UMLogError(#"Module `%#` is not an UMEventEmitter, thus it cannot be subscribed to.", moduleName);
return;
}
id<UMEventEmitter> eventEmitter = (id<UMEventEmitter>)module;
// Per-module observing state
int newModuleListenersCount = [self moduleListenersCountFor:moduleName] - 1;
if (newModuleListenersCount == 0) {
[eventEmitter stopObserving];
} else if (newModuleListenersCount < 0) {
UMLogError(#"Attempted to remove more `%#` listeners than added", moduleName);
newModuleListenersCount = 0;
}
_modulesListenersCounts[moduleName] = [NSNumber numberWithInt:newModuleListenersCount];
// Global observing state
if (_listenersCount - 1 < 0) {
UMLogError(#"Attempted to remove more proxied event emitter listeners than added");
_listenersCount = 0;
} else {
_listenersCount -= 1;
}
if (_listenersCount == 0) {
[self stopObserving];
}
}
# pragma mark Utilities
- (int)moduleListenersCountFor:(NSString *)moduleName
{
NSNumber *moduleListenersCountNumber = _modulesListenersCounts[moduleName];
int moduleListenersCount = 0;
if (moduleListenersCountNumber != nil) {
moduleListenersCount = [moduleListenersCountNumber intValue];
}
return moduleListenersCount;
}
# pragma mark - UMModuleRegistryConsumer
- (void)setModuleRegistry:(UMModuleRegistry *)moduleRegistry
{
moduleRegistry = moduleRegistry;
}
#end
I know little to nothing about Objective-c so any help is appreciated a code example is double appreciated!
Related
I have integrated amazon chime sdk with my react native application following this guide.
https://github.com/aws-samples/amazon-chime-react-native-demo
I am new to react native and have poor understanding of native development. I need to integrate Screen Sharing and Meeting recording in my application and I am stuck for a week now. Any help will be much appreciated.
These are the files containing the functions
MeetingObservers.h
#import <AmazonChimeSDK/AmazonChimeSDK-Swift.h>
#import <Foundation/Foundation.h>
#class NativeMobileSDKBridge;
#define kEventOnMeetingStart #"OnMeetingStart"
#define kEventOnMeetingEnd #"OnMeetingEnd"
#define kEventOnAttendeesJoin #"OnAttendeesJoin"
#define kEventOnAttendeesLeave #"OnAttendeesLeave"
#define kEventOnAttendeesMute #"OnAttendeesMute"
#define kEventOnAttendeesUnmute #"OnAttendeesUnmute"
#define kEventOnAddVideoTile #"OnAddVideoTile"
#define kEventOnRemoveVideoTile #"OnRemoveVideoTile"
#define kEventOnDataMessageReceive #"OnDataMessageReceive"
#define kEventOnError #"OnError"
#define kErrorEventOnMaximumConcurrentVideoReached #"OnMaximumConcurrentVideoReached"
#define sVideoAtCapacityViewOnly 206
#interface MeetingObservers : NSObject <RealtimeObserver, VideoTileObserver, AudioVideoObserver, DataMessageObserver>
- (id)initWithBridge:(NativeMobileSDKBridge *) bridge logger:(ConsoleLogger * )logger;
#end
MeetingObservers.m
//
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
//
#import <Foundation/Foundation.h>
#import "MeetingObservers.h"
#import "NativeMobileSDKBridge.h"
#implementation MeetingObservers
{
NativeMobileSDKBridge* _bridge;
ConsoleLogger * _logger;
}
- (id)initWithBridge: (NativeMobileSDKBridge *) bridge logger:(ConsoleLogger * )logger
{
_bridge = bridge;
_logger = logger;
return self;
}
- (void)attendeesDidJoinWithAttendeeInfo:(NSArray<AttendeeInfo *> * _Nonnull)attendeeInfo
{
for (id currentAttendeeInfo in attendeeInfo)
{
[_bridge sendEventWithName:kEventOnAttendeesJoin body:#{#"attendeeId":[currentAttendeeInfo attendeeId], #"externalUserId":[currentAttendeeInfo externalUserId]}];
[_logger infoWithMsg:[NSString stringWithFormat:#"Attendee %# join", [currentAttendeeInfo externalUserId]]];
}
}
- (void)attendeesDidLeaveWithAttendeeInfo:(NSArray<AttendeeInfo *> * _Nonnull)attendeeInfo
{
for (id currentAttendeeInfo in attendeeInfo)
{
[_bridge sendEventWithName:kEventOnAttendeesLeave body:#{#"attendeeId":[currentAttendeeInfo attendeeId], #"externalUserId":[currentAttendeeInfo externalUserId]}];
[_logger infoWithMsg:[NSString stringWithFormat:#"AttendeeQuit(leave) : %# ", [currentAttendeeInfo externalUserId]]];
}
}
- (void)attendeesDidMuteWithAttendeeInfo:(NSArray<AttendeeInfo *> * _Nonnull)attendeeInfo
{
for (id currentAttendeeInfo in attendeeInfo)
{
[_bridge sendEventWithName:kEventOnAttendeesMute body:[currentAttendeeInfo attendeeId]];
[_logger infoWithMsg:[NSString stringWithFormat:#"Attendee %# mute", [currentAttendeeInfo externalUserId]]];
}
}
- (void)attendeesDidUnmuteWithAttendeeInfo:(NSArray<AttendeeInfo *> * _Nonnull)attendeeInfo
{
for (id currentAttendeeInfo in attendeeInfo)
{
[_bridge sendEventWithName:kEventOnAttendeesUnmute body:[currentAttendeeInfo attendeeId]];
[_logger infoWithMsg:[NSString stringWithFormat:#"Attendee %# unmute", [currentAttendeeInfo externalUserId]]];
}
}
- (void)signalStrengthDidChangeWithSignalUpdates:(NSArray<SignalUpdate *> * _Nonnull)signalUpdates
{
for (id currentSignalUpdate in signalUpdates)
{
[_logger infoWithMsg:[NSString stringWithFormat:#"Attendee %# signalStrength changed to %lu", [[currentSignalUpdate attendeeInfo] attendeeId], [currentSignalUpdate signalStrength]]];
}
}
- (void)volumeDidChangeWithVolumeUpdates:(NSArray<VolumeUpdate *> * _Nonnull)volumeUpdates
{
for (id currentVolumeUpdate in volumeUpdates)
{
[_logger infoWithMsg:[NSString stringWithFormat:#"Attendee %# volumeLevel changed to %ld", [[currentVolumeUpdate attendeeInfo] attendeeId], [currentVolumeUpdate volumeLevel]]];
}
}
- (void)attendeesDidDropWithAttendeeInfo:(NSArray<AttendeeInfo *> * _Nonnull)attendeeInfo
{
for (id currentAttendeeInfo in attendeeInfo)
{
[_bridge sendEventWithName:kEventOnAttendeesLeave body:#{#"attendeeId":[currentAttendeeInfo attendeeId], #"externalUserId":[currentAttendeeInfo externalUserId]}];
[_logger infoWithMsg:[NSString stringWithFormat:#"AttendeeQuit(drop) : %# ", [currentAttendeeInfo externalUserId]]];
}
}
- (void)videoTileDidAddWithTileState:(VideoTileState * _Nonnull)tileState
{
[_bridge sendEventWithName:kEventOnAddVideoTile body:#{#"tileId":[NSNumber numberWithInt: (int)tileState.tileId], #"isLocal":#(tileState.isLocalTile), #"isScreenShare":#(tileState.isContent), #"attendeeId":tileState.attendeeId, #"pauseState":[NSNumber numberWithInt: (int)tileState.pauseState], #"videoStreamContentHeight":[NSNumber numberWithInt: (int)tileState.videoStreamContentHeight], #"videoStreamContentWidth":[NSNumber numberWithInt: (int)tileState.videoStreamContentWidth]}];
}
- (void)videoTileDidPauseWithTileState:(VideoTileState * _Nonnull)tileState
{
// Not implemented for demo purposes
}
- (void)videoTileDidRemoveWithTileState:(VideoTileState * _Nonnull)tileState
{
[_bridge sendEventWithName:kEventOnRemoveVideoTile body:#{#"tileId":[NSNumber numberWithInt: (int)tileState.tileId], #"isLocal":#(tileState.isLocalTile), #"isScreenShare":#(tileState.isContent)}];
}
- (void)videoTileDidResumeWithTileState:(VideoTileState * _Nonnull)tileState
{
// Not implemented for demo purposes
}
- (void)videoTileSizeDidChangeWithTileState:(VideoTileState * _Nonnull)tileState {
// Not implemented for demo purposes
}
- (void)audioSessionDidCancelReconnect
{
// Not implemented for demo purposes
}
- (void)audioSessionDidStartConnectingWithReconnecting:(BOOL)reconnecting
{
// Not implemented for demo purposes
}
- (void)audioSessionDidStartWithReconnecting:(BOOL)reconnecting
{
if (!reconnecting)
{
[_logger infoWithMsg:#"Meeting Started!"];
[_bridge sendEventWithName:kEventOnMeetingStart body:nil];
}
}
- (void)audioSessionDidStopWithStatusWithSessionStatus:(MeetingSessionStatus * _Nonnull)sessionStatus
{
// Not implemented for demo purposes
}
- (void)connectionDidBecomePoor
{
// Not implemented for demo purposes
}
- (void)connectionDidRecover
{
// Not implemented for demo purposes
}
- (void)videoSessionDidStartConnecting
{
// Not implemented for demo purposes
}
- (void)videoSessionDidStartWithStatusWithSessionStatus:(MeetingSessionStatus * _Nonnull)sessionStatus
{
if (sessionStatus.statusCode == sVideoAtCapacityViewOnly)
{
[_bridge sendEventWithName:kEventOnError body:kErrorEventOnMaximumConcurrentVideoReached];
}
}
- (void)videoSessionDidStopWithStatusWithSessionStatus:(MeetingSessionStatus * _Nonnull)sessionStatus
{
// Not implemented for demo purposes
}
- (void)audioSessionDidDrop
{
// Not implemented for demo purposes
}
- (void)remoteVideoSourcesDidBecomeAvailableWithSources:(NSArray<RemoteVideoSource *> * _Nonnull)sources {
// Not implemented for demo purposes
}
- (void)remoteVideoSourcesDidBecomeUnavailableWithSources:(NSArray<RemoteVideoSource *> * _Nonnull)sources {
// Not implemented for demo purposes
}
- (void)dataMessageDidReceivedWithDataMessage:(DataMessage *)dataMessage {
[_bridge sendEventWithName:kEventOnDataMessageReceive body:#{
#"data":[dataMessage text],
#"topic":[dataMessage topic],
#"senderAttendeeId":[dataMessage senderAttendeeId],
#"senderExternalUserId":[dataMessage senderExternalUserId],
#"throttled":#(dataMessage.throttled),
#"timestampMs":#(dataMessage.timestampMs)
}];
}
#end
NativeMobileSDKBridge.h
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#import <AmazonChimeSDK/AmazonChimeSDK-Swift.h>
#define kMeetingId #"MeetingId"
#define kExternalMeetingId #"ExternalMeetingId"
#define kMediaRegion #"MediaRegion"
#define kAttendeeId #"AttendeeId"
#define kExternalUserId #"ExternalUserId"
#define kJoinToken #"JoinToken"
#define kMediaPlacement #"MediaPlacement"
#define kAudioFallbackUrl #"AudioFallbackUrl"
#define kAudioHostUrl #"AudioHostUrl"
#define kTurnControlUrl #"TurnControlUrl"
#define kSignalingUrl #"SignalingUrl"
#interface NativeMobileSDKBridge : RCTEventEmitter <RCTBridgeModule>
#end
NativeMobileSDKBridge.m
#import "NativeMobileSDKBridge.h"
#import <AVFoundation/AVFoundation.h>
#import "RNVideoViewManager.h"
#import <AmazonChimeSDKMedia/AmazonChimeSDKMedia.h>
#import "MeetingObservers.h"
#import <React/RCTUIManager.h>
#implementation NativeMobileSDKBridge
static DefaultMeetingSession *meetingSession;
static ConsoleLogger *logger;
RCT_EXPORT_MODULE();
- (NSArray<NSString *> *)supportedEvents
{
return
#[
kEventOnMeetingStart,
kEventOnMeetingEnd,
kEventOnAttendeesJoin,
kEventOnAttendeesLeave,
kEventOnAttendeesMute,
kEventOnAttendeesUnmute,
kEventOnAddVideoTile,
kEventOnRemoveVideoTile,
kEventOnDataMessageReceive,
kEventOnError
];
}
# pragma mark: Native Function
RCT_EXPORT_METHOD(startMeeting:(NSDictionary *)meetingInfoDict attendeeInfo:(NSDictionary *)attendeeInfoDict)
{
if (meetingSession != nil)
{
[meetingSession.audioVideo stop];
meetingSession = nil;
}
logger = [[ConsoleLogger alloc] initWithName:#"NativeMobileSDKBridge" level:LogLevelDEFAULT];
[logger infoWithMsg: [[NSString alloc] initWithFormat:#"Running Amazon Chime SDK (%#)", Versioning.sdkVersion]];
// Parse meeting join data from payload
NSDictionary *mediaPlacementDict = [meetingInfoDict objectForKey:kMediaPlacement];
// Parse meeting info
NSString *meetingId = [meetingInfoDict objectForKey:kMeetingId];
NSString *externalMeetingId = [meetingInfoDict objectForKey:kExternalMeetingId];
NSString *meetingRegion = [meetingInfoDict objectForKey:kMediaRegion];
// Parse meeting join info
NSString *audioFallbackUrl = [mediaPlacementDict objectForKey:kAudioFallbackUrl];
NSString *audioHostUrl = [mediaPlacementDict objectForKey:kAudioHostUrl];
NSString *turnControlUrl = [mediaPlacementDict objectForKey:kTurnControlUrl];
NSString *signalingUrl = [mediaPlacementDict objectForKey:kSignalingUrl];
// Parse attendee info
NSString *attendeeId = [attendeeInfoDict objectForKey:kAttendeeId];
NSString *externalUserId = [attendeeInfoDict objectForKey:kExternalUserId];
NSString *joinToken = [attendeeInfoDict objectForKey:kJoinToken];
// Initialize meeting session through AmazonChimeSDK
MediaPlacement *mediaPlacement = [[MediaPlacement alloc] initWithAudioFallbackUrl:audioFallbackUrl
audioHostUrl:audioHostUrl
signalingUrl:signalingUrl
turnControlUrl:turnControlUrl];
Meeting *meeting = [[Meeting alloc] initWithExternalMeetingId:externalMeetingId
mediaPlacement:mediaPlacement
mediaRegion:meetingRegion
meetingId:meetingId];
CreateMeetingResponse *createMeetingResponse = [[CreateMeetingResponse alloc] initWithMeeting:meeting];
Attendee *attendee = [[Attendee alloc] initWithAttendeeId:attendeeId
externalUserId:externalUserId joinToken:joinToken];
CreateAttendeeResponse *createAttendeeResponse = [[CreateAttendeeResponse alloc] initWithAttendee:attendee];
MeetingSessionConfiguration *meetingSessionConfiguration = [[MeetingSessionConfiguration alloc] initWithCreateMeetingResponse:createMeetingResponse
createAttendeeResponse:createAttendeeResponse];
meetingSession = [[DefaultMeetingSession alloc] initWithConfiguration:meetingSessionConfiguration
logger:logger];
[self startAudioClient];
}
RCT_EXPORT_METHOD(stopMeeting)
{
[meetingSession.audioVideo stop];
meetingSession = nil;
[self sendEventWithName:kEventOnMeetingEnd body: nil];
}
RCT_EXPORT_METHOD(setMute:(BOOL)isMute)
{
BOOL success = true;
if (isMute)
{
success = [meetingSession.audioVideo realtimeLocalMute];
}
else
{
success = [meetingSession.audioVideo realtimeLocalUnmute];
}
if (!success)
{
[self sendEventWithName:kEventOnError body:#"Failed to set mute state"];
}
}
RCT_EXPORT_METHOD(setCameraOn:(BOOL)isOn)
{
if (isOn)
{
[self startVideo];
}
else
{
[meetingSession.audioVideo stopLocalVideo];
}
}
RCT_EXPORT_METHOD(bindVideoView:(NSNumber * _Nonnull)viewIdentifier tileId:(NSNumber * _Nonnull)tileId)
{
dispatch_async(dispatch_get_main_queue(), ^{
UIView* view = [self.bridge.uiManager viewForReactTag:viewIdentifier];
[meetingSession.audioVideo bindVideoViewWithVideoView:(DefaultVideoRenderView*)view tileId:[tileId integerValue]];
});
}
RCT_EXPORT_METHOD(unbindVideoView:(NSNumber * _Nonnull)tileId)
{
dispatch_async(dispatch_get_main_queue(), ^{
[meetingSession.audioVideo unbindVideoViewWithTileId:[tileId integerValue]];
});
}
#pragma mark: Media Related Function
-(void)startVideo
{
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (status)
{
case AVAuthorizationStatusNotDetermined:
{
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
{
if (granted)
{ // Access has been granted ..retry starting video
[self startVideo];
} else { // Access denied
[self sendEventWithName:kEventOnError body:#"User denied camera permission"];
}
}];
break;
}
case AVAuthorizationStatusAuthorized:
{
NSError* error;
[meetingSession.audioVideo startLocalVideoAndReturnError:&error];
if(error != nil)
{
[self sendEventWithName:kEventOnError body:#"Fail to start local video"];
}
break;
}
case AVAuthorizationStatusDenied:
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
break;
}
default:
break;
}
}
-(void)startAudioClient
{
if (meetingSession == nil)
{
[logger errorWithMsg:#"meetingSession is not initialized"];
return;
}
MeetingObservers* observer = [[MeetingObservers alloc] initWithBridge:self logger:logger];
[meetingSession.audioVideo addRealtimeObserverWithObserver:observer];
[meetingSession.audioVideo addVideoTileObserverWithObserver:observer];
[meetingSession.audioVideo addAudioVideoObserverWithObserver:observer];
[meetingSession.audioVideo addRealtimeDataMessageObserverWithTopic:#"chat" observer:observer];
[self startAudioVideo];
}
-(void)startAudioVideo
{
NSError* error = nil;
BOOL started = [meetingSession.audioVideo startAndReturnError:&error];
if (started && error == nil)
{
[logger infoWithMsg:#"RN meeting session was started successfully"];
[meetingSession.audioVideo startRemoteVideo];
}
else
{
NSString *errorMsg = [NSString stringWithFormat:#"Failed to start meeting, error: %#", error.description];
[logger errorWithMsg:errorMsg];
// Handle missing permission error
if ([error.domain isEqual:#"AmazonChimeSDK.PermissionError"])
{
AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
if (permissionStatus == AVAudioSessionRecordPermissionUndetermined)
{
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted)
{
if (granted)
{
[logger infoWithMsg:#"Audio permission granted"];
// Retry after permission is granted
[self startAudioVideo];
}
else
{
[logger infoWithMsg:#"Audio permission not granted"];
[self sendEventWithName:kEventOnMeetingEnd body:nil];
}
}];
}
else if (permissionStatus == AVAudioSessionRecordPermissionDenied)
{
[logger errorWithMsg:#"User did not grant permission, should redirect to Settings"];
[self sendEventWithName:kEventOnMeetingEnd body:nil];
}
}
else
{
// Uncaught error
[self sendEventWithName:kEventOnError body: errorMsg];
[self sendEventWithName:kEventOnMeetingEnd body:nil];
}
}
}
RCT_EXPORT_METHOD(sendDataMessage:(NSString* _Nonnull)topic data:(NSString* _Nonnull)data lifetimeMs:(int)lifetimeMs)
{
if (meetingSession == nil) {
return;
}
[meetingSession.audioVideo realtimeSendDataMessageWithTopic:topic data:data lifetimeMs:lifetimeMs error:nil];
}
#end
I am trying to integrate the functionality of screen sharing and recording meeting but I am not familiar with native programming
I want to check if the user has an open basket if so send them to the menu if not then send send them to my open basket view but i m getting an error.
So my question is how to get rid of this problem ? and is my logic right ?
'NSInvalidArgumentException', reason: '-[Merchant merchantId]: unrecognized selector sent to instance 0x7fad19529490'.
i m using this method to check for open baskets when the button addToOrderButtonTapped is press in the menuview
[[OrderManager sharedManager]getBasketsForMerchant:merchant success:^(NSArray *baskets) {
for (Basket *basket in baskets){
if ([basket.status isEqualToString:kBasketStatusOpen]) {
[self.openBaskets addObject:basket];
}
}if (self.openBaskets.count > 0){
self.openBaskets = self.openBaskets;
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:kSegueMenu sender:self];
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:KSegueOpenBasket sender:self];
});
}
} failure:^(NSError *error, NSHTTPURLResponse *response) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *localizedString = NSLocalizedString(#"Order.ErrorLoadingOpenOrders", "Get error message");
[self showMessage:localizedString withTitle:MessageTypeError];
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
});
}];
The menuview
//
// MenuItemViewController.m
// BaseApp
//
#import "MenuItemViewController.h"
#import "RoundedButton.h"
#import "MenuOptionCell.h"
#import "MenuItem.h"
#import "MenuOptionButton.h"
#import "MenuChoice.h"
#import "OrderManager.h"
#import "TypeOfChoiceCell.h"
#import "MenuOption.h"
#import "OrderPricingTableViewCell.h"
#import "RKManagedObjectStore.h"
#import "NSManagedObjectContext+RKAdditions.h"
#import "MerchantManager.h"
#import "Merchant.h"
#import "OrdersViewController.h"
#import "MenuItem.h"
#import "MenuViewController.h"
#import "OrderConfirmationViewController.h"
#import "OrderManager.h"
#import "APIClient.h"
#import "MenuManager.h"
#import "DiscoverViewController.h"
#import "DiscoverCollectionViewCell.h"
#import "MerchantManager.h"
#import "MenuViewController.h"
#import "MenuManager.h"
#import "MerchantDetailsViewController.h"
#import "OpenOrdersViewController.h"
typedef NS_ENUM(NSUInteger, MenuOptionsSection) {
MenuOptionsSectionRequired = 0,
MenuOptionsSectionOptional = 1
};
static NSString *const OpenBasketsSegue = #"OpenBaskets";
static NSString *const kSegueMenu = #"ShowMenu";
static NSString *const KSegueOpenBasket = #"OpenBaskets";
#interface MenuItemViewController () <UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) IBOutlet UILabel *menuItemName;
#property (nonatomic, weak) IBOutlet UILabel *quantityLabel;
#property (nonatomic, weak) IBOutlet UILabel *menuItemPrice;
#property (nonatomic, weak) IBOutlet UILabel *subTotalLabel;
#property (nonatomic, weak) IBOutlet UITextView *menuItemDescription;
#property (nonatomic, weak) IBOutlet RoundedButton *addToOrderButton;
#property (nonatomic, weak) IBOutlet UITableView *tableView;
#property (nonatomic, weak) IBOutlet UIView *cartView;
#property (nonatomic, weak) IBOutlet UIButton *subtractButton;
#property (nonatomic) NSDecimalNumber *temporarySubtotal;
#property (nonatomic, strong) NSMutableArray<MenuOption *> *requiredChoices;
#property (nonatomic, strong) NSMutableArray<MenuOption *> *optionalChoices;
#property (nonatomic, strong) NSMutableArray<NSMutableArray *> *optionChoicesSection;
#property (nonatomic, strong) NSMutableArray<NSDictionary *> *options;
#property (nonatomic, strong) NSMutableArray <Basket *>*openBaskets;
#property (nonatomic) BOOL launchViewFirstTime;
#end
#implementation MenuItemViewController
#pragma - Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"Menu Item";
self.menuItemName.text = self.menuCategory.selectedBasketLine.menuItem.name;
self.menuItemPrice.text = [NSString stringWithFormat:#"%#", [Basket formattedCurrencyStringForAmount:self.menuCategory.selectedBasketLine.menuItem.price]];
self.quantityLabel.text = self.menuCategory.selectedBasketLine.quantity.stringValue;
self.temporarySubtotal = [self calculateTemporarySubtotal:[OrderManager sharedManager].currentBasket.subtotal menuItemPrice:self.menuCategory.selectedBasketLine.menuItem.price];
[self setSubtotalText:self.temporarySubtotal];
self.menuItemDescription.text = self.menuCategory.selectedBasketLine.menuItem.menuItemDescription;
self.subtractButton.alpha = 0.65;
self.requiredChoices = [[NSMutableArray alloc] init];
self.optionalChoices = [[NSMutableArray alloc] init];
self.optionChoicesSection = [[NSMutableArray alloc] init];
self.options = [[NSMutableArray alloc] init];
[self initializeChoiceArrays];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.launchViewFirstTime = YES;
if (self.optionChoicesSection.count > 0) {
[self.tableView reloadData];
} else {
self.tableView.hidden = YES;
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.launchViewFirstTime = NO;
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
for (MenuOption *menuOption in self.requiredChoices) {
[menuOption resetNumberOfChoicesSelected];
}
for (MenuOption *menuOption in self.optionalChoices) {
[menuOption resetNumberOfChoicesSelected];
}
self.menuCategory = nil;
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self.menuItemDescription setContentOffset:CGPointZero animated:YES];
}
#pragma - IBActions
- (IBAction)addButtonTapped:(id)sender {
NSInteger count = self.quantityLabel.text.integerValue;
count++;
if (count > 1) {
self.subtractButton.alpha = 1;
}
NSDecimalNumber *newSubTotal = [self.temporarySubtotal decimalNumberByAdding:self.menuCategory.selectedBasketLine.menuItem.price];
[self modifyCurrentBasketSubtotal:newSubTotal quantity:count];
}
- (IBAction)subtractButtonTapped:(id)sender {
NSInteger count = self.quantityLabel.text.integerValue;
if (count > 1) {
count--;
NSDecimalNumber *newSubTotal = [self.temporarySubtotal decimalNumberBySubtracting:self.menuCategory.selectedBasketLine.menuItem.price];
[self modifyCurrentBasketSubtotal:newSubTotal quantity:count];
if (count == 1) {
self.subtractButton.alpha = 0.65;
}
}
}
- (IBAction)addToOrderButtonTapped:(id)sender {
MenuOption *menuOption;
Merchant *merchant = [[Merchant alloc]init];
// First check if there are any missing required options that have to be selected
if ((menuOption = [self checkMissingRequiredOptionsHaveBeenSelected])) {
NSString *localizedString = NSLocalizedString(#"AddMenuItem.RequiredChoicesNotSelected", #"Get string for error");
NSString *formattedString = [NSString stringWithFormat:localizedString, menuOption.name];
[self showMessage:formattedString withTitle:MessageTypeError];
return;
}
// Now check if the minimum and maximum choice seletion have been met for the menu options
if ((menuOption = [self validateMinMaxForMenuOptionsHaveBeenMet])) {
NSString *localizedString = NSLocalizedString(#"AddMenuItem.MinMaxNotFulfilled", #"Get string for error");
NSString *formattedString = [NSString stringWithFormat:localizedString, menuOption.name];
[self showMessage:formattedString withTitle:MessageTypeError];
return;
}
// Add the menu item to the basket
if (self.menuItemAddedBlock) {
self.menuItemAddedBlock(self, self.menuCategory);
// [self dismissViewControllerAnimated:YES completion:nil];
//checking for open basket here
[[OrderManager sharedManager]getBasketsForMerchant:merchant success:^(NSArray *baskets) {
for (Basket *basket in baskets){
if ([basket.status isEqualToString:kBasketStatusOpen]) {
[self.openBaskets addObject:basket];
}
}if (self.openBaskets.count > 0){
self.openBaskets = self.openBaskets;
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:kSegueMenu sender:self];
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:KSegueOpenBasket sender:self];
});
}
} failure:^(NSError *error, NSHTTPURLResponse *response) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *localizedString = NSLocalizedString(#"Order.ErrorLoadingOpenOrders", "Get error message");
[self showMessage:localizedString withTitle:MessageTypeError];
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
});
}];
}
}
- (IBAction)cancelButtonTapped:(UIBarButtonItem *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma - UITableViewDelegate, UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.optionChoicesSection.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.optionChoicesSection[section].count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MenuOptionCell *cell = (MenuOptionCell *)[tableView dequeueReusableCellWithIdentifier:[MenuOptionCell reuseIdentifier]];
cell.menuOption = self.optionChoicesSection[indexPath.section][indexPath.row];
cell.menuOptionCellButtonPressedBlock = ^(MenuOptionButton *button, MenuOption *option, MenuChoice *choice) {
[self adjustSelectedOptions:option choice:choice indexPath:indexPath];
};
cell.menuOptionCellDefaultOptionDetectedBlock = ^(MenuOptionButton *button, MenuOption *option, MenuChoice *choice) {
[self adjustSelectedOptions:option choice:choice indexPath:indexPath];
};
cell.menuOptionCellDeselectedBlock = ^(MenuOptionButton *button, MenuOption *option, MenuChoice *choice) {
[self adjustSelectedOptions:option choice:choice indexPath:indexPath];
};
[cell configureCell:self.launchViewFirstTime];
return cell;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
TypeOfChoiceCell *cell = (TypeOfChoiceCell *)[tableView dequeueReusableCellWithIdentifier:[TypeOfChoiceCell reuseIdentifier]];
switch ((MenuOptionsSection)section) {
case MenuOptionsSectionRequired:
if (self.requiredChoices.count > 0) {
cell.title = NSLocalizedString(#"AddMenuItem.RequiredChoiceText", #"Get string for title");
return cell;
} else if (self.optionalChoices.count > 0) {
cell.title = NSLocalizedString(#"AddMenuItem.OptionalChoiceText", #"get string for title");
return cell;
}
case MenuOptionsSectionOptional:
if (self.optionalChoices.count > 0) {
cell.title = NSLocalizedString(#"AddMenuItem.OptionalChoiceText", #"Get string for title");
return cell;
}
}
return nil;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
MenuOption *option = self.optionChoicesSection[indexPath.section][indexPath.row];
return [MenuOptionCell heightForMenuOption:option];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return [TypeOfChoiceCell height];
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0.01f;
}
#pragma - Helpers
- (void)setSubtotalText:(NSDecimalNumber *)subtotal {
if (!subtotal) {
self.subTotalLabel.text = NSLocalizedString(#"AddMenuItem.SubtotalDefault", #"Get string for subtotal");
} else {
NSString *localizedString = NSLocalizedString(#"AddMenuItem.SubtotalFormatted", #"Get string for subtotal");
self.subTotalLabel.text = [NSString stringWithFormat:localizedString, [Basket formattedCurrencyStringForAmount:subtotal]];
}
}
- (void)modifyCurrentBasketSubtotal:(NSDecimalNumber *)subtotal quantity:(NSInteger)quantity {
self.menuCategory.selectedBasketLine.quantity = [NSNumber numberWithInteger:quantity];
self.menuCategory.selectedBasketLine.subtotal = subtotal;
self.temporarySubtotal = subtotal;
[self setSubtotalText:subtotal];
self.quantityLabel.text = self.menuCategory.selectedBasketLine.quantity.stringValue;
}
- (NSDecimalNumber *)calculateTemporarySubtotal:(NSDecimalNumber *)orderManagerSubTotal menuItemPrice:(NSDecimalNumber *)menuItemPrice {
if (orderManagerSubTotal == 0) {
return menuItemPrice;
} else {
return [orderManagerSubTotal decimalNumberByAdding:menuItemPrice];
}
}
- (MenuOption *)checkMissingRequiredOptionsHaveBeenSelected {
for (MenuOption *menuOption in self.requiredChoices) {
if (!menuOption.selected) {
return menuOption;
}
}
return nil;
}
- (MenuOption *)validateMinMaxForMenuOptionsHaveBeenMet {
for (MenuOption *menuOption in self.requiredChoices) {
if ([menuOption validateIndividualMinMaxForMenuOption]) {
return menuOption;
}
}
for (MenuOption *menuOption in self.optionalChoices) {
if (menuOption.selected) {
if ([menuOption validateIndividualMinMaxForMenuOption]) {
return menuOption;
}
}
}
return nil;
}
- (void)initializeChoiceArrays {
NSArray<MenuOption *> *menuOptions = [self.menuCategory.selectedBasketLine.menuItem.menuOptions allObjects];
for (MenuOption *menuOption in menuOptions) {
if (menuOption.minimumChoices == nil) {
menuOption.minimumChoices = [NSNumber numberWithInt:0];
}
if (menuOption.maximumChoices == nil) {
menuOption.maximumChoices = [NSNumber numberWithInt:0];
}
// For now make an optional choice required if minimumChoices > 0
if (menuOption.isRequired || [menuOption.minimumChoices intValue] > 0) {
menuOption.isRequired = YES;
[self.requiredChoices addObject:menuOption];
} else {
[self.optionalChoices addObject:menuOption];
}
}
if (self.requiredChoices.count > 0) {
[self.optionChoicesSection addObject:self.requiredChoices];
}
if (self.optionalChoices.count > 0) {
[self.optionChoicesSection addObject:self.optionalChoices];
}
}
- (void)adjustSelectedOptions:(MenuOption *)option choice:(MenuChoice *)choice indexPath:(NSIndexPath *)indexPath {
self.menuCategory.selectedBasketLine.subtotal = self.menuCategory.selectedBasketLine.menuItem.price;
if (option.selected && option.menuChoices.count == 0) {
[self.options addObject:#{kOption: option.menuOptionId ?: #"", kChoice: #""}];
if (option.price) {
self.temporarySubtotal = [self.temporarySubtotal decimalNumberByAdding:option.price];
}
if (option.isRequired) {
self.requiredChoices[indexPath.row].selected = option.selected;
self.requiredChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
} else {
self.optionalChoices[indexPath.row].selected = option.selected;
self.optionalChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
}
} else {
if (option.menuChoices.count == 0) {
[self.options removeObject:#{kOption: option.menuOptionId ?: #"", kChoice: #""}];
if (option.price) {
self.temporarySubtotal = [self.temporarySubtotal decimalNumberBySubtracting:option.price];
}
if (option.isRequired) {
self.requiredChoices[indexPath.row].selected = option.selected;
self.requiredChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
} else {
self.optionalChoices[indexPath.row].selected = option.selected;
self.optionalChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
}
}
}
if (choice.selected && option.menuChoices.count > 0) {
[self.options addObject:#{kOption: choice.menuOption.menuOptionId ?: #"", kChoice: choice.menuChoiceId ?: #""}];
if (choice.price) {
self.temporarySubtotal = [self.temporarySubtotal decimalNumberByAdding:choice.price];
}
if (option.isRequired) {
self.requiredChoices[indexPath.row].selected = choice.selected;
self.requiredChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
} else {
self.optionalChoices[indexPath.row].selected = choice.selected;
self.optionalChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
}
} else {
if (option.menuChoices.count > 0) {
[self.options removeObject:#{kOption: choice.menuOption.menuOptionId ?: #"", kChoice: choice.menuChoiceId ?: #""}];
if (choice.price) {
self.temporarySubtotal = [self.temporarySubtotal decimalNumberBySubtracting:choice.price];
}
if (option.isRequired) {
if ([option.numberOfChoicesSelected intValue] == 0) {
self.requiredChoices[indexPath.row].selected = option.selected;
} else {
self.requiredChoices[indexPath.row].selected = !choice.selected;
}
self.requiredChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
} else {
self.optionalChoices[indexPath.row].selected = choice.selected;
self.optionalChoices[indexPath.row].numberOfChoicesSelected = option.numberOfChoicesSelected;
}
}
}
[self setSubtotalText:self.temporarySubtotal];
self.menuCategory.selectedBasketLine.attributes = self.options;
}
#end
This is the line of code thats giving me the error
+ (void)getBasketsForMerchant:(Merchant *)merchant success:(void (^)(NSArray *basket))success failure:(void (^)(NSError *, NSHTTPURLResponse *))failure {
NSMutableDictionary* params = #{#"expand": #"merchant"}.mutableCopy;
if (merchant) {
params[#"merchant"] = merchant.merchantId;
}
[[RKObjectManager sharedManager] getObjectsAtPath:kOrdersEndpoint parameters:params success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult){
NSArray* items = mappingResult.array;
if (success) {
success(items);
}
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
if (failure) {
failure(error, operation.HTTPRequestOperation.response);
} else {
_defaultFailureBlock(operation, error);
}
}];
}
This error:
'NSInvalidArgumentException', reason: '-[Merchant merchantId]: unrecognized selector sent to instance 0x7fad19529490'.
Means that you have an object at location 0x7fad19529490 and you tried to call "merchantId" on it. That object does not respond to merchantId.
So, look very carefully at the definition of Merchant. Did you get the spelling of merchantId right in fieldMappings? Is it merchantID with a capital D?
If you are sure that merchant has a property named merchantId exactly, then, the next likely thing is that the object at 0x7fad19529490 is not a Merchant.
The easiest thing to do is to add an exception breakpoint (use the +) at the bottom of the breakpoint navigator. When this exception happens, grab the pointer address from the error (it might be different each time) and see what it is in the debugger. To do that, at the (lldb) prompt, type:
po (NSObject*)(0x7fad19529490)
but use the address from the error. If it's a Merchant, I expect it to say something like:
<Merchant: 0x7fad19529490>
Or, if you have overridden description, it will be the output of that.
I have a custom UIProgressView class (from John Rommel Estropia) which works perfect on 64 bit iPhone simulator and device. But when I run it on 32bit simulators/devices it did not show on the screen? Any idea?
This problem also valid for another class for drawing circular progress.
Here is .m file:
#import "SPKProgress.h"
#interface SPKProgress()
#property (nonatomic, weak) UIImageView *trackImageView;
#property (nonatomic, weak) UIImageView *progressImageView;
#end
#implementation SPKProgress
#pragma mark - NSObject
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setupProgressView];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setupProgressView];
}
return self;
}
#pragma mark - UIView
- (void)layoutSubviews
{
[super layoutSubviews];
UIImageView *trackImageView = self.trackImageView;
UIImageView *progressImageView = self.progressImageView;
if (!trackImageView || !progressImageView)
{
return;
}
CGRect bounds = self.bounds;
CGFloat boundsTop = CGRectGetMinY(bounds);
UIImage *trackImage = self.trackImage;
if (trackImage)
{
CGRect trackFrame = trackImageView.frame;
CGFloat trackHeight = trackImage.size.height;
trackImageView.frame = (CGRect){
.origin.x = CGRectGetMinX(trackFrame),
.origin.y = (boundsTop
+ ((CGRectGetHeight(bounds) - trackHeight) * 0.5f)),
.size.width = CGRectGetWidth(trackFrame),
.size.height = trackHeight
};
}
UIImage *progressImage = self.progressImage;
if (progressImage)
{
CGRect progressFrame = progressImageView.frame;
CGFloat progressHeight = progressImage.size.height;
progressImageView.frame = (CGRect){
.origin.x = CGRectGetMinX(progressFrame),
.origin.y = (boundsTop
+ ((CGRectGetHeight(bounds) - progressHeight) * 0.5f)),
.size.width = CGRectGetWidth(progressFrame),
.size.height = progressHeight
};
}
}
#pragma mark - UIProgressView
- (void)setProgressImage:(UIImage *)progressImage
{
[super setProgressImage:progressImage];
self.progressImageView.image = progressImage;
}
- (void)setTrackImage:(UIImage *)trackImage
{
[super setTrackImage:trackImage];
self.trackImageView.image = trackImage;
}
#pragma mark - private
- (void)setupProgressView
{
if ([self compareVersionString:[UIDevice currentDevice].systemVersion
withVersionString:#"7.1"] == NSOrderedAscending)
{
return;
}
NSArray *subviews = self.subviews;
if ([subviews count] != 2)
{
return;
}
for (UIView *subview in subviews)
{
if (![subview isKindOfClass:[UIImageView class]])
{
return;
}
}
self.trackImageView = subviews[0];
self.progressImageView = subviews[1];
self.trackImageView.image = self.trackImage;
self.progressImageView.image = self.progressImage;
}
- (NSComparisonResult)compareVersionString:(NSString *)versionString1
withVersionString:(NSString *)versionString2
{
NSArray *components1 = [versionString1 componentsSeparatedByString:#"."];
NSArray *components2 = [versionString2 componentsSeparatedByString:#"."];
NSUInteger components1Count = [components1 count];
NSUInteger components2Count = [components2 count];
NSUInteger partCount = MAX(components1Count, components2Count);
for (NSInteger part = 0; part < partCount; ++part)
{
if (part >= components1Count)
{
return NSOrderedAscending;
}
if (part >= components2Count)
{
return NSOrderedDescending;
}
NSString *part1String = components1[part];
NSString *part2String = components2[part];
NSInteger part1 = [part1String integerValue];
NSInteger part2 = [part2String integerValue];
if (part1 > part2)
{
return NSOrderedDescending;
}
if (part1 < part2)
{
return NSOrderedAscending;
}
}
return NSOrderedSame;
}
#end
And the header file:
#import <UIKit/UIKit.h>
#interface SPKProgress : UIProgressView
#end
For some reason NSString objects just won't go into an NSMutableSet
The set remains empty - count stays 0 and firstInHandler is never called
I have tried everything..!
If anyone has any idea why I would greatly appreciate the help
VCKeySet.h
#import <Foundation/Foundation.h>
#interface VCKeySet : NSObject
{
void (^firstInHandler)();
void (^lastOutHandler)();
}
#property (atomic, readonly, strong) NSMutableSet* keys;
- (id) initWithFirstInHandler:(void(^)()) firstInHandler_ withLastOutHandler:(void(^)()) lastOutHandler_;
- (void) add:(NSString*) key_;
- (void) remove: (NSString*) key_;
- (BOOL) has: (NSString*) key;
#end
VCKeySet.m
#import "VCKeySet.h"
#implementation VCKeySet
#synthesize keys;
- (id) initWithFirstInHandler: (void(^)()) firstInHandler_ withLastOutHandler: (void(^)()) lastOutHandler_
{
if(self=[self init])
{
firstInHandler = firstInHandler_;
lastOutHandler = lastOutHandler_;
}
return self;
}
- (void) add:(NSString*) key_
{
for(NSString* key in self.keys)
{
if([key isEqualToString:key_])
{
return;
}
}
[self.keys addObject:key_];
if([self.keys count] == 1)
{
firstInHandler();
}
}
- (void) remove: (NSString*) key_
{
for(NSString* key in keys)
{
if([key isEqualToString:key_])
{
[keys removeObject:key];
if([keys count] == 0)
{
lastOutHandler();
}
}
}
}
- (BOOL) has: (NSString*) key_
{
for(NSString* key in keys)
{
if([key isEqualToString:key_])
{
return YES;
}
}
return NO;
}
#end
Here is how I try to add a key to the set
VCKeySet* lock = [[VCKeySet alloc] initWithFirstInHandler:^()
{
NSLog(#"Adding UI lock");
[application beginIgnoringInteractionEvents];
}
withLastOutHandler:^()
{
NSLog(#"Removing UI lock");
[application endIgnoringInteractionEvents];
}];
[lock add:#"InitialiseApp"];
Thanks
The problem is, you never allocate and initialize your set.
Refactor your add: method to look something like this:
- (void) add:(NSString*) key_ {
if (!self.keys) {
_keys = [[NSMutableSet alloc] init];
}
[self.keys addObject:key_];
if([self.keys count] == 1) {
firstInHandler();
}
}
I'm using an ARC enabled objective c version of protocol buffer and I have integrated it with XCode 4.5. I compiled a simple proto file, serialized and deserialized it to check if its working fine.
Now i added a "repeated" type field and i'm getting the following error -
ARC Semantic Issue: No visible #interface for "PBAppendableArray" declares the selector "objectAtIndex:"
My proto file -
message Person
{
required int32 id=1;
required string name=2;
repeated string email=3;
}
Person.pb.h
// Generated by the protocol buffer compiler. DO NOT EDIT!
#import <ProtocolBuffers/ProtocolBuffers.h>
#class Person;
#class Person_Builder;
#ifndef __has_feature
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif // __has_feature
#ifndef NS_RETURNS_NOT_RETAINED
#if __has_feature(attribute_ns_returns_not_retained)
#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
#else
#define NS_RETURNS_NOT_RETAINED
#endif
#endif
#interface PersonRoot : NSObject {
}
+ (PBExtensionRegistry*) extensionRegistry;
+ (void) registerAllExtensions:(PBMutableExtensionRegistry*) registry;
#end
#interface Person : PBGeneratedMessage {
#private
BOOL hasId_:1;
BOOL hasName_:1;
int32_t id;
NSString* name;
PBAppendableArray * emailArray;
}
- (BOOL) hasId;
- (BOOL) hasName;
#property (readonly) int32_t id;
#property (readonly, strong) NSString* name;
#property (readonly, strong) PBArray * email;
- (NSString*)emailAtIndex:(NSUInteger)index;
+ (Person*) defaultInstance;
- (Person*) defaultInstance;
- (BOOL) isInitialized;
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output;
- (Person_Builder*) builder;
+ (Person_Builder*) builder;
+ (Person_Builder*) builderWithPrototype:(Person*) prototype;
- (Person_Builder*) toBuilder;
+ (Person*) parseFromData:(NSData*) data;
+ (Person*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (Person*) parseFromInputStream:(NSInputStream*) input;
+ (Person*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
+ (Person*) parseFromCodedInputStream:(PBCodedInputStream*) input;
+ (Person*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
#end
#interface Person_Builder : PBGeneratedMessage_Builder {
#private
Person* result;
}
- (Person*) defaultInstance;
- (Person_Builder*) clear;
- (Person_Builder*) clone;
- (Person*) build;
- (Person*) buildPartial;
- (Person_Builder*) mergeFrom:(Person*) other;
- (Person_Builder*) mergeFromCodedInputStream:(PBCodedInputStream*) input;
- (Person_Builder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry;
- (BOOL) hasId;
- (int32_t) id;
- (Person_Builder*) setId:(int32_t) value;
- (Person_Builder*) clearId;
- (BOOL) hasName;
- (NSString*) name;
- (Person_Builder*) setName:(NSString*) value;
- (Person_Builder*) clearName;
- (PBAppendableArray *)email;
- (NSString*)emailAtIndex:(NSUInteger)index;
- (Person_Builder *)addEmail:(NSString*)value;
- (Person_Builder *)setEmailArray:(NSArray *)array;
- (Person_Builder *)setEmailValues:(const NSString* *)values count:(NSUInteger)count;
- (Person_Builder *)clearEmail;
#end
Person.pb.m
// Generated by the protocol buffer compiler. DO NOT EDIT!
#import "Person.pb.h"
#implementation PersonRoot
static PBExtensionRegistry* extensionRegistry = nil;
+ (PBExtensionRegistry*) extensionRegistry {
return extensionRegistry;
}
+ (void) initialize {
if (self == [PersonRoot class]) {
PBMutableExtensionRegistry* registry = [PBMutableExtensionRegistry registry];
[self registerAllExtensions:registry];
extensionRegistry = registry;
}
}
+ (void) registerAllExtensions:(PBMutableExtensionRegistry*) registry {
}
#end
#interface Person ()
#property int32_t id;
#property (strong) NSString* name;
#property (strong) PBAppendableArray * emailArray;
#end
#implementation Person
- (BOOL) hasId {
return !!hasId_;
}
- (void) setHasId:(BOOL) value {
hasId_ = !!value;
}
#synthesize id;
- (BOOL) hasName {
return !!hasName_;
}
- (void) setHasName:(BOOL) value {
hasName_ = !!value;
}
#synthesize name;
#synthesize emailArray;
#dynamic email;
- (id) init {
if ((self = [super init])) {
self.id = 0;
self.name = #"";
}
return self;
}
static Person* defaultPersonInstance = nil;
+ (void) initialize {
if (self == [Person class]) {
defaultPersonInstance = [[Person alloc] init];
}
}
+ (Person*) defaultInstance {
return defaultPersonInstance;
}
- (Person*) defaultInstance {
return defaultPersonInstance;
}
- (NSArray *)email {
return emailArray;
}
- (NSString*)emailAtIndex:(NSUInteger)index {
return [emailArray objectAtIndex:index];
}
- (BOOL) isInitialized {
if (!self.hasId) {
return NO;
}
if (!self.hasName) {
return NO;
}
return YES;
}
- (void) writeToCodedOutputStream:(PBCodedOutputStream*) output {
if (self.hasId) {
[output writeInt32:1 value:self.id];
}
if (self.hasName) {
[output writeString:2 value:self.name];
}
const NSUInteger emailArrayCount = self.emailArray.count;
if (emailArrayCount > 0) {
const NSString* *values = (const NSString* *)self.emailArray.data;
for (NSUInteger i = 0; i < emailArrayCount; ++i) {
[output writeString:3 value:values[i]];
}
}
[self.unknownFields writeToCodedOutputStream:output];
}
- (int32_t) serializedSize {
int32_t size = memoizedSerializedSize;
if (size != -1) {
return size;
}
size = 0;
if (self.hasId) {
size += computeInt32Size(1, self.id);
}
if (self.hasName) {
size += computeStringSize(2, self.name);
}
{
int32_t dataSize = 0;
const NSUInteger count = self.emailArray.count;
const NSString* *values = (const NSString* *)self.emailArray.data;
for (NSUInteger i = 0; i < count; ++i) {
dataSize += computeStringSizeNoTag(values[i]);
}
size += dataSize;
size += 1 * count;
}
size += self.unknownFields.serializedSize;
memoizedSerializedSize = size;
return size;
}
+ (Person*) parseFromData:(NSData*) data {
return (Person*)[[[Person builder] mergeFromData:data] build];
}
+ (Person*) parseFromData:(NSData*) data extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
return (Person*)[[[Person builder] mergeFromData:data extensionRegistry:extensionRegistry] build];
}
+ (Person*) parseFromInputStream:(NSInputStream*) input {
return (Person*)[[[Person builder] mergeFromInputStream:input] build];
}
+ (Person*) parseFromInputStream:(NSInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
return (Person*)[[[Person builder] mergeFromInputStream:input extensionRegistry:extensionRegistry] build];
}
+ (Person*) parseFromCodedInputStream:(PBCodedInputStream*) input {
return (Person*)[[[Person builder] mergeFromCodedInputStream:input] build];
}
+ (Person*) parseFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
return (Person*)[[[Person builder] mergeFromCodedInputStream:input extensionRegistry:extensionRegistry] build];
}
+ (Person_Builder*) builder {
return [[Person_Builder alloc] init];
}
+ (Person_Builder*) builderWithPrototype:(Person*) prototype {
return [[Person builder] mergeFrom:prototype];
}
- (Person_Builder*) builder {
return [Person builder];
}
- (Person_Builder*) toBuilder {
return [Person builderWithPrototype:self];
}
- (void) writeDescriptionTo:(NSMutableString*) output withIndent:(NSString*) indent {
NSUInteger listCount = 0;
if (self.hasId) {
[output appendFormat:#"%#%#: %#\n", indent, #"id", [NSNumber numberWithInt:self.id]];
}
if (self.hasName) {
[output appendFormat:#"%#%#: %#\n", indent, #"name", self.name];
}
for (NSString* element in self.emailArray) {
[output appendFormat:#"%#%#: %#\n", indent, #"email", element];
}
[self.unknownFields writeDescriptionTo:output withIndent:indent];
}
- (BOOL) isEqual:(id)other {
if (other == self) {
return YES;
}
if (![other isKindOfClass:[Person class]]) {
return NO;
}
Person *otherMessage = other;
return
self.hasId == otherMessage.hasId &&
(!self.hasId || self.id == otherMessage.id) &&
self.hasName == otherMessage.hasName &&
(!self.hasName || [self.name isEqual:otherMessage.name]) &&
[self.emailArray isEqualToArray:otherMessage.emailArray] &&
(self.unknownFields == otherMessage.unknownFields || (self.unknownFields != nil && [self.unknownFields isEqual:otherMessage.unknownFields]));
}
- (NSUInteger) hash {
NSUInteger hashCode = 7;
NSUInteger listCount = 0;
if (self.hasId) {
hashCode = hashCode * 31 + [[NSNumber numberWithInt:self.id] hash];
}
if (self.hasName) {
hashCode = hashCode * 31 + [self.name hash];
}
for (NSString* element in self.emailArray) {
hashCode = hashCode * 31 + [element hash];
}
hashCode = hashCode * 31 + [self.unknownFields hash];
return hashCode;
}
#end
#interface Person_Builder()
#property (strong) Person* result;
#end
#implementation Person_Builder
#synthesize result;
- (id) init {
if ((self = [super init])) {
self.result = [[Person alloc] init];
}
return self;
}
- (PBGeneratedMessage*) internalGetResult {
return result;
}
- (Person_Builder*) clear {
self.result = [[Person alloc] init];
return self;
}
- (Person_Builder*) clone {
return [Person builderWithPrototype:result];
}
- (Person*) defaultInstance {
return [Person defaultInstance];
}
- (Person*) build {
[self checkInitialized];
return [self buildPartial];
}
- (Person*) buildPartial {
Person* returnMe = result;
self.result = nil;
return returnMe;
}
- (Person_Builder*) mergeFrom:(Person*) other {
if (other == [Person defaultInstance]) {
return self;
}
if (other.hasId) {
[self setId:other.id];
}
if (other.hasName) {
[self setName:other.name];
}
if (other.emailArray.count > 0) {
if (result.emailArray == nil) {
result.emailArray = [[NSMutableArray alloc] initWithArray:other.emailArray];
} else {
[result.emailArray addObjectsFromArray:other.emailArray];
}
}
[self mergeUnknownFields:other.unknownFields];
return self;
}
- (Person_Builder*) mergeFromCodedInputStream:(PBCodedInputStream*) input {
return [self mergeFromCodedInputStream:input extensionRegistry:[PBExtensionRegistry emptyRegistry]];
}
- (Person_Builder*) mergeFromCodedInputStream:(PBCodedInputStream*) input extensionRegistry:(PBExtensionRegistry*) extensionRegistry {
PBUnknownFieldSet_Builder* unknownFields = [PBUnknownFieldSet builderWithUnknownFields:self.unknownFields];
while (YES) {
int32_t tag = [input readTag];
switch (tag) {
case 0:
[self setUnknownFields:[unknownFields build]];
return self;
default: {
if (![self parseUnknownField:input unknownFields:unknownFields extensionRegistry:extensionRegistry tag:tag]) {
[self setUnknownFields:[unknownFields build]];
return self;
}
break;
}
case 8: {
[self setId:[input readInt32]];
break;
}
case 18: {
[self setName:[input readString]];
break;
}
case 26: {
[self addEmail:[input readString]];
break;
}
}
}
}
- (BOOL) hasId {
return result.hasId;
}
- (int32_t) id {
return result.id;
}
- (Person_Builder*) setId:(int32_t) value {
result.hasId = YES;
result.id = value;
return self;
}
- (Person_Builder*) clearId {
result.hasId = NO;
result.id = 0;
return self;
}
- (BOOL) hasName {
return result.hasName;
}
- (NSString*) name {
return result.name;
}
- (Person_Builder*) setName:(NSString*) value {
result.hasName = YES;
result.name = value;
return self;
}
- (Person_Builder*) clearName {
result.hasName = NO;
result.name = #"";
return self;
}
- (NSMutableArray *)email {
return result.emailArray;
}
- (NSString*)emailAtIndex:(NSUInteger)index {
return [result emailAtIndex:index];
}
- (Person_Builder *)addEmail:(NSString*)value {
if (result.emailArray == nil) {
result.emailArray = [[NSMutableArray alloc]init];
}
[result.emailArray addObject:value];
return self;
}
- (Person_Builder *)setEmailArray:(NSArray *)array {
result.emailArray = [[NSMutableArray alloc] initWithArray:array];
return self;
}
- (Person_Builder *)clearEmail {
result.emailArray = nil;
return self;
}
#end
Leaving this answer for people who might check this qn later.
It's better to use the C++ protobuf files directly in Xcode. Required fields will not compile at all in the mentioned ios-version of protobuf. Check this answer to add protobuf in your project.