How to properly use a protocol-defined property in Realm predicates? - objective-c

I am trying to implement a cache system using Realm.
Depending on classes, the cache should have different lengths. I've therefore defined a StalenessChecking protocol:
#protocol StalenessChecking <NSObject>
#required
// nonatomic needed when using a getter
#property (readonly, nonatomic, getter=isStale) bool stale;
#optional
- (void) setStaleness: (NSTimeInterval) duration;
#end
and an object:
Interface file (DziObject.h)
#import "Realm.h"
#import "StalenessChecking.h"
#interface DziObject : RLMObject <StalenessChecking>
#property (readonly) NSDate* refresh;
#end
Implementation file (DiObject.m)
#import 'DziObject.h'
#implementation DziObject
{
NSTimeInterval stalenessInterval;
}
#synthesize stale = _stale;
- (instancetype)init
{
self = [super init];
if (self) {
_refresh = [NSDate date];
stalenessInterval = 120.0;
}
return self;
}
- (bool) isStale {
return [[NSDate dateWithTimeInterval:stalenessInterval sinceDate:_refresh] timeIntervalSinceReferenceDate]< [[NSDate date] timeIntervalSinceReferenceDate];
}
- (void)setStaleness:(NSTimeInterval) duration
{
stalenessInterval = duration;
}
#end
I then call them from a Facade:
Interface:
#import <Foundation/Foundation.h>
#interface SDK_Facade : NSObject
+ (void) createDziO;
#end
Implementation:
#import "SDK_Facade.h"
#import "DziObject.h"
#implementation SDK_Facade
+ (void) createDziO
{
DziObject *dziO = [[DziObject alloc] init];
// both work fine
if (dziO.isStale) {
NSLog(#"Is stale");
}
if (dziO.stale) {
NSLog(#"Is really stale");
}
// Query Realm for all results less than 2 minutes old
// TODO: -- currently crashes
RLMResults< DziObject *> * dzios = [DziObject objectsWhere:#"stale == %#", #YES];
NSLog(#"DziOs: %lu", (unsigned long)dzios.count);
// Persist your data easily
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
[realm addObject:dziO];
}];
// Queries should update in realtime
NSLog(#"DO: %lu", (unsigned long)dzios.count);
}
#end
I get an ugly crash:
Terminating app due to uncaught exception 'Invalid property name', reason: 'Property 'stale' not found in object of type 'DziObject'

You can't define a Realm model property in a protocol that the model class conforms to. It must be done in the class definition itself.
#interface DziObject : RLMObject <StalenessChecking>
#property NSDate* refresh;
#property (getter=isStale) bool stale;
#end

Related

App crashing while working with class and not going to catch

In my AppDelegate.m, I am doing something like this
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#try {
// initalizing Meeting config
MeetingConfig *config = [[MeetingConfig alloc] init];
NSLog(#"Initalized Meeting Config: %#", config);
[config setRoomName:#"test123"];
NSLog(#"SetRoom name for Meeting config: %#", config.roomName);
NSString *clientId = #"";
NSLog(#"Unused Client id is: %#", clientId);
//Call UIView from here
}#catch (NSException *exception) {
NSLog(#"exception: %#", exception);
}
return YES;
}
Where my MeetingConfig.m file looks like this
#implementation MeetingConfig
- (id) init
{
if (self = [super init]) {
self.apiBase = #"https://api.in";
self.showSetupScreen = false;
self.autoTune = true;
}
return self;
}
- (void) setAuthToken:(NSString *)authToken
{
self.authToken = authToken;
}
- (void) setApiBase:(NSString *)apiBase
{
self.apiBase = apiBase;
}
// more code
and MeetingConfig looks like this
#import <Foundation/Foundation.h>
#interface MeetingConfig : NSObject
#property (nonatomic, assign) NSString* roomName;
#property (nonatomic, assign) NSString* authToken;
#property (nonatomic, assign)Boolean autoTune;
#property (nonatomic, assign)NSString* apiBase;
#property (nonatomic, assign)Boolean showSetupScreen;
- (void) setRoomName:(NSString *)roomName;
- (void) setAuthToken:(NSString *)authToken;
- (void) setShowSetupScreen:(Boolean)showSetupScreen;
- (void) setAutoTuneEnabled:(Boolean)autoTune;
- (id) init;
#end
Can someone help me in determining what I could be doing wrong here? and why doesn't it log exception in NSLog? Also, I am super new to objective C (i have been asked to stick with Objective c) and if anyone have any suggestion in regards to the code then please let me know.
Error
You're using assign for reference/pointer types: #property retain, assign, copy, nonatomic in Objective-C
They should probably be declared copy, because this is a kind of value object, I think.
No exceptions were caught because no exceptions were thrown. Throwing/catching exceptions for control flow is not common in Objective-C
You don't need to write explicit setter functions for #properties
You should prefer to use BOOL type instead of Boolean, with values of YES/NO instead of true/false.
You should return instancetype not id from init, at least in reasonably modern Objective C
Consider making an initialiser that takes all the properties (initWithRoomName:clientID:) and make them read only once set
You don't need to declare -(id) init in your header since it gets that from NSObject

Why am I getting the superclass and not the subclass in my method call?

I have this class, which is a port from a C# abstract class; this is the .h file:
#interface Schedule : NSObject {
}
#property (strong, nonatomic) NSDate *apptStartTime;
#property (strong, nonatomic) NSDate *apptEndTime;
#property (strong, nonatomic) NSString *key;
-(BOOL) occursOnDate: (NSDate *) timeOfAppointment;
#end
This is the .m file for Schedule:
#implementation Schedule {
}
#synthesize apptStartTime;
#synthesize apptEndTime;
#synthesize key;
/**
The OccursOnDate method is abstract and must be implemented by subclasses. When passed a date, the schedulers must determine if an appointment falls on that date. If one does, the method should return true. If not, the method returns false.
*/
-(BOOL) occursOnDate: (NSDate *) dateOfAppointment {
return YES:
}
Because it's a C# abstract class, it has to be overridden (or subclassed) which I have done here (this is the .h file):
#interface SingleSchedule : Schedule {
}
#property (strong,nonatomic) NSDate *apptDate;
-(BOOL) occursOnDate: (NSDate *)date;
#end
This is the .m file:
#implementation SingleSchedule {
}
#synthesize apptDate;
-(BOOL) occursOnDate: (NSDate *)date {
return (apptDate == date); // <--------- TODO where is apptDate set?
}
This is where I call the occursOnDate class, expecting to get the subcclass, but I get the superclass class instead:
-(void) addAppointmentsForDate:(NSDate *)checkDate scheduleSet: (NSMutableSet *)setOfSchedules appointmentSet:(NSMutableSet *)setOfAppts {
Schedule *sc = [[Schedule alloc]init];
Appointment *newAppt = [[Appointment alloc]init];
NSArray *scheduleArray = [setOfSchedules allObjects];
for(int i = 0; i < scheduleArray.count; i++) {
if([sc occursOnDate: checkDate]) { // <-------- method called is the superclass, not the override
newAppt = [self generateAppointment:checkDate andSchedule: scheduleArray [i]];
[setOfAppts addObject:newAppt];
}
}
}
What am I missing here to be able to get the subclass method and not the other? (I have looked at SO and Google but found nothing which would answer this question exactly).
The base class implementation is being called because sc is of type Schedule, not SingleSchedule. When you instantiate a class, the new object is aware of it's own implementation and it's base class chain, but the object is not aware of it's inherited classes.
Maybe what you want is:
SingleSchedule *sc = [[SingleSchedule alloc]init];

Getting an error on my (first ever) CardGameViewController

I'm getting the error incompatible pointer types assigning to Deck *__strong from PlayCards *
And i'm not sure why is that. Its in the first method implemented (deck):
#import "CardGameViewController.h"
#import "PlayingCards.h"
#interface CardGameViewController ()
#property (weak, nonatomic) IBOutlet UILabel *cardLabel;
#property (nonatomic) NSUInteger flipsCount;
#property (strong, nonatomic) Deck *deck;
#end
#implementation CardGameViewController
-(Deck *) deck {
if (!_deck) _deck = [[PlayingCards alloc] init];
return _deck;
}
-(void) setFlipsCount:(NSUInteger)flipsCount {
_flipsCount = flipsCount;
self.cardLabel.text = [NSString stringWithFormat:#"Flips:%d", self.flipsCount];
}
- (IBAction)flipCard:(UIButton *)sender {
sender.selected = !sender.isSelected;
self.flipsCount++;
}
#end
This is the header file(nothing going on here):
#import <UIKit/UIKit.h>
//#import "Card.h"
//#import "Deck.h"
//#import "PlayingCards.h"
#interface CardGameViewController : UIViewController
#end
And the PlayingCard class inheriting from Deck class..
this is the PlayingCards.m
#import "PlayingCards.h"
#implementation PlayingCards
#synthesize suit = _suit;
//modifying the contents getter so it will return array with the ranks and rank+suit
-(NSString *) contents {
NSArray *cardsRank = [PlayingCards rankStrings];
return [cardsRank[self.rank] stringByAppendingString:self.suit];
}
//creating a method to make sure we get validated suits
+(NSArray *) validSuit {
return #[#"♠",#"♣",#"♥",#"♦"];
}
//creating calss method to validate the rank
+(NSArray *) rankStrings {
return #[#"?",#"A",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
//creating a new setter for suit to make sure we get the valitated suits, uding the validateSuit method
-(void) setSuit:(NSString *)suit {
if ([[PlayingCards validSuit] containsObject:suit]) {
_suit = suit;
}
}
//creating new getter for suit to make sure its not empty
-(NSString *) suit {
return _suit? _suit: #"?";
}
//creating a class method to make sure when user set the rank he will will
+(NSUInteger) maxRank {
return [self rankStrings].count - 1;
}
//creating a new setter to the renk to make sure the rank is validates
-(void) setRank:(NSUInteger)rank {
if (rank <= [PlayingCards maxRank]) {
_rank = rank;
}
}
#end
PlayingCards.h
#import "Card.h"
#import "Deck.h"
#interface PlayingCards : Card
#property (strong, nonatomic) NSString *suit;
#property (nonatomic) NSUInteger rank;
+(NSArray *) validSuit;
+(NSUInteger) maxRank;
#end
This line:
if (!_deck) _deck = [[PlayingCards alloc] init];
Should be:
if (!_deck) _deck = [[PlayingCardDeck alloc] init];
If the parent for Card is of class NSObject as you say, and given that PlayingCards inherits from Card, then you can't assign an instance of PlayingCards to a variable of type Deck*. That's what the compiler is telling you.
If you really need to do it, you have to write:
if (!_deck) _deck = (Deck*)[[PlayingCards alloc] init];
It would only be valid because in Objective-C the implementation is given at runtime and which method of which class is called is only decided at runtime when the message is dispatched. However, this pattern is very unusual and you better be certain that PlayingCards is implementing all the selectors that might be called on a Deck instance. A better way would be to use protocols.
You can define a protocol and then use:
id <myProtocol> deck = [[PlayingCards alloc] init];
Put in the protocol all the selectors you need.
Why can't you use this ?
PlayingCards* deck = [[PlayingCards alloc] init];

NSOperation with repeat option

I've developed a Category that gives the NSOperation the ability to be executed in the background at timed intervals. I would really appreciate getting some feedback on this, especially any potential problems with this approach that I'm not thinking of.
Thank you!
Here's the code:
NSOperation+Repeat.h
#import <Foundation/Foundation.h>
#interface NSOperation (repeat)
#property (readonly, nonatomic) NSTimeInterval repeatInterval;
#property (readonly, nonatomic) NSOperationQueue *repeatOperationQueue;
- (void)performUsingOperationQueue:(NSOperationQueue *)operationQueue;
- (void)performAtRepeatingInterval:(NSTimeInterval)interval usingOperationQueue:(NSOperationQueue *)operationQueue;
#end
NSOperation+Repeat.m
#import "NSOperation+repeat.h"
#import <objc/runtime.h>
static char const * const RepeatPropertiesKey = "RepeatProperties";
#implementation NSOperation (repeat)
#dynamic repeatInterval;
#dynamic repeatOperationQueue;
static NSString * RepeatIntervalKey = #"interval";
static NSString * RepeatOperationQueueKey = #"operationQueue";
static NSString * RepeatTimerKey = #"timer";
- (NSMutableDictionary *)repeatProperties {
NSMutableDictionary * properties = objc_getAssociatedObject(self, RepeatPropertiesKey);
if (properties == nil) {
properties = [NSMutableDictionary new];
objc_setAssociatedObject(self, RepeatPropertiesKey, properties, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return properties;
}
- (NSTimeInterval)interval {
NSNumber * interval = [[self repeatProperties] objectForKey:RepeatIntervalKey];
return [interval doubleValue];
}
- (NSOperationQueue *)repeatOperationQueue {
NSOperationQueue * operationQueue = [[self repeatProperties] objectForKey:RepeatOperationQueueKey];
return operationQueue;
}
- (void)performUsingOperationQueue:(NSOperationQueue *)operationQueue {
[operationQueue addOperation:[self copy]];
}
- (void)performAtInterval:(NSTimer *)timer {
[self performUsingOperationQueue:self.repeatOperationQueue];
}
- (void)performAtRepeatingInterval:(NSTimeInterval)interval usingOperationQueue:(NSOperationQueue *)operationQueue {
// Save interval and operationQueue in repeatProperties
[self.repeatProperties setValue:[NSNumber numberWithDouble:interval] forKey:RepeatIntervalKey];
[self.repeatProperties setValue:operationQueue forKey:RepeatOperationQueueKey];
// Create timer to call performAtInterval on self
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:(interval*60)
target:self
selector:#selector(performAtInterval:)
userInfo:nil
repeats:YES];
// Save the timer in repeatProperties
[self.repeatProperties setValue:timer forKey:RepeatTimerKey];
[self performUsingOperationQueue:operationQueue];
}
#end
Here's an example of a NSOperation subclass that can repeat:
MSScheduleImportOperation.h
#import <Foundation/Foundation.h>
#import "NSOperation+Repeat.h"
#interface MSScheduleImportOperation : NSOperation <NSCopying>
#property (readonly, strong, nonatomic) NSString* employeeId;
- (id)initWithEmployeeId:(NSString *)employeeId;
#end
MSScheduleImportOperation.m
#import "MSScheduleImportOperation.h"
#implementation MSScheduleImportOperation
#synthesize employeeId = __employeeId;
- (id)initWithEmployeeId:(NSString *)employeeId {
self = [super init];
__employeeId = [employeeId copy];
return self;
}
- (id)copyWithZone:(NSZone *)zone {
MSScheduleImportOperation* copy = [[MSScheduleImportOperation alloc] initWithEmployeeId:self.employeeId];
return copy;
}
- (void)main
{
...
}
#end
The Apple documentation says:
An operation object is a single-shot object—that is, it executes its task once and cannot be used to execute it again.
So the first problem is that there might be internals that stop it from working. Although, I see you try to get around the problem by making a copy.
This leads us to the other problem is that NSOperation is not advertised to conform to NSCopying.
[operationQueue addOperation:[self copy]];
This line should throw an exception.
Instead of a category on NSOperation where the object copies itself and adds the copy to an NSOperationQueue - it would be simpler to manage this at a higher level. For example:
+[RepeatingOperation operationBlock:(InitOperationBlock)operationBlock
queue:(NSOperationQueue*)queue
interval:(NSTimeInterval)interval];
where InitOperationBlock would be a block where the operation was created and configured.
The main benefit is the API would be harder to mess up. For example in category in the original post, performUsingOperationQueue: will fail silently if you forget to set repeatOperationQueue.

How to create a strategy pattern in Objective-C?

I need to develop a strategy pattern where i have a main class with other three classes where i need to refer to the objects of the other three classes using the main class object.To solve this is the strategy pattern will help me? If so please do give me the syntax in Objective-C?
You'll want to look at Objective-C's protocol mechanism. Here's a simple protocol with a single required method:
#protocol Strategy <NSObject>
#required
- (void) execute;
#end
Then you declare a class that fulfills that protocol:
#interface ConcreteStrategyA : NSObject <Strategy>
{
// ivars for A
}
#end
The implementation must provide the -execute method (since it was declared as #required):
#implementation ConcreteStrategyA
- (void) execute
{
NSLog(#"Called ConcreteStrategyA execute method");
}
#end
You can make a similar ConcreteStrategyB class, but I'm not going to show it here.
Finally, make a context class with a property maintaining the current strategy.
#interface Context : NSObject
{
id<Strategy> strategy;
}
#property (assign) id<Strategy> strategy;
- (void) execute;
#end
Here is the implementation. The method that delegates to the strategy's -execute method just happens to be called -execute as well, but it doesn't have to be.
#implementation Context
#synthesize strategy;
- (void) execute
{
[strategy execute];
}
#end
Now I'll make a few instances and put them to use:
ConcreteStrategyA * concreteStrategyA = [[[ConcreteStrategyA alloc] init] autorelease];
ConcreteStrategyB * concreteStrategyB = [[[ConcreteStrategyB alloc] init] autorelease];
Context * context = [[[Context alloc] init] autorelease];
[context setStrategy:concreteStrategyA];
[context execute];
[context setStrategy:concreteStrategyB];
[context execute];
The console output shows that the strategy was successfully changed:
2010-02-09 19:32:56.582 Strategy[375:a0f] Called ConcreteStrategyA execute method
2010-02-09 19:32:56.584 Strategy[375:a0f] Called ConcreteStrategyB execute method
Note that if the protocol does not specify #required, the method is optional. In this case, the context needs to check whether the strategy implements the method:
- (void) execute
{
if ([strategy respondsToSelector:#selector(execute)])
[strategy execute];
}
This is a common Cocoa pattern called delegation. For more information on delegation and other design patterns in Cocoa, see this.
Here's a bit more of a concrete example. You can put each item in a separate file. I've put it all in one file for ease of understanding.
// main.m
// StrategyWikipediaExample
//
// Created by steve on 2014-07-08.
// Copyright (c) 2014 steve. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
Equivalent to Java Interface
All concrete Strategies conform to this protocol
*/
#protocol MathOperationsStrategy<NSObject>
- (void)performAlgorithmWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second;
#end
/**
Concrete Strategies.
Java would say they "Extend" the interface.
*/
#interface AddStrategy : NSObject<MathOperationsStrategy>
#end
#implementation AddStrategy
- (void)performAlgorithmWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second
{
NSInteger result = first + second;
NSLog(#"Adding firstNumber: %ld with secondNumber: %ld yields : %ld", first, second, result);
}
#end
#interface SubtractStrategy : NSObject<MathOperationsStrategy>
#end
#implementation SubtractStrategy
- (void)performAlgorithmWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second
{
NSInteger result = first - second;
NSLog(#"Subtracting firstNumer: %ld with secondNumber: %ld yields: %ld", first, second, result);
}
#end
#interface MultiplyStrategy : NSObject<MathOperationsStrategy>
#end
#implementation MultiplyStrategy
- (void)performAlgorithmWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second
{
NSInteger result = first * second;
NSLog(#"Multiplying firstNumber: %ld with secondNumber: %ld yields: %ld", first, second, result);
}
#end
#interface Context : NSObject
#property (weak, nonatomic)id<MathOperationsStrategy>strategy; // reference to concrete strategy via protocol
- (id)initWithMathOperationStrategy:(id<MathOperationsStrategy>)strategy; // setter
- (void)executeWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second;
#end
#implementation Context
- (id)initWithMathOperationStrategy:(id<MathOperationsStrategy>)strategy
{
if (self = [super init]) {
_strategy = strategy;
}
return self;
}
- (void)executeWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second
{
[self.strategy performAlgorithmWithFirstNumber:first secondNumber:second];
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
id<MathOperationsStrategy>addStrategy = [AddStrategy new];
Context *contextWithAdd = [[Context alloc] initWithMathOperationStrategy:addStrategy];
[contextWithAdd executeWithFirstNumber:10 secondNumber:10];
}
return 0;
}