Convert subscript function from Objective C to Swift - objective-c

I am trying to convert some Objective C code to Swift and can't manage to do it right with subscripting.
This is the method I am trying to migrate to Swift:
- (NSArray *)rangesOfSubstringAlphaNumeric:(NSString *)substring rangesLimit:(NSUInteger)rangesLimit {
NSAssert(rangesLimit, #"A range limit grather than 0 must be specified");
if (!substring.length) {
return nil;
}
static NSCharacterSet * restrictedCharacters = nil;
if (!restrictedCharacters) {
restrictedCharacters = [[NSCharacterSet alphanumericCharacterSet] invertedSet];
}
NSArray * substrings = [substring componentsSeparatedByCharactersInSet:restrictedCharacters];
NSMutableArray * allRanges = [NSMutableArray array];
NSString *searchedString = self;
for (NSString *stringToMatch in substrings) {
if (![stringToMatch isEqualToString:#""]) {
NSRange aRange;
NSUInteger lastLocation = 0;
NSUInteger foundRanges = 0;
while (foundRanges++ < rangesLimit &&
(aRange = [searchedString localizedStandardRangeOfString:stringToMatch]).location != NSNotFound) {
searchedString = [searchedString substringFromIndex:aRange.location + aRange.length];
aRange.location = aRange.location + lastLocation;
lastLocation = aRange.location + aRange.length;
[allRanges addObject:[NSValue valueWithRange:aRange]];
}
}
}
return allRanges.count ? [allRanges copy] : nil;
}
I got stuck on the subscripting part since it seems I cannot assign integer values to Indexes and conversion from Index to Int is out of hand for me I'm kind of stuck, this is what I managed to do:
func rangesOfAlphanumeric(substring: String, limit: UInt) -> [Range<String.Index>] {
guard limit > 0, !substring.isEmpty else {
if limit == 0 {
assert(false, "limit must be greather than 0")
}
return []
}
var searchedString = self
let substrings = substring.components(separatedBy: NSCharacterSet.restricted)
for stringToMatch in substrings {
if !stringToMatch.isEmpty {
// var aRange: Range<String.Index>?
// var lastLocation: UInt = 0
// var foundRanges: UInt = 0
// while foundRanges < limit,
// let tempRange = searchedString.localizedStandardRange(of: stringToMatch),
// !tempRange.isEmpty {
//
// searchedString = String(searchedString[tempRange.upperBound...])
// if let lastLocation = lastLocation {
// aRange = temp
// }
// }
}
}
}
UPDATE: Solution below.

Managed to resolve the issue using the ranges function posted here:
func rangesOfAlphanumeric(substring: String) -> [Range<String.Index>] {
var searchedString = self
let substrings = substring.components(separatedBy: NSCharacterSet.restricted)
return substrings.compactMap { (stringToMatch) -> [Range<String.Index>]? in
guard !stringToMatch.isEmpty else {
return nil
}
let ranges = searchedString.ranges(of: stringToMatch, options: [
.diacriticInsensitive,
.caseInsensitive
])
if let lastRange = ranges.last {
searchedString = String(searchedString[index(after: lastRange.upperBound)])
}
return ranges
}.flatMap{$0}
}

I created this repo with swift 5 is very easy to use
all is already set up. you have just to change the IAP ids
The Github repo

Related

How to extract performance metrics measured by measureBlock in XCTest

I have a simple test function which will tap a button an measure the performance. I'm using XCTest. After measureBlock returns I can see a bunch of perf-metrics on the console. I would like to get this within the test-program such that I can populate the data somewhere else programmatically. Watching the test data on test-console is proving to be slow because I have a lot of test-cases.
- (void)testUseMeasureBlock {
XCUIElement *launchTest1Button = [[XCUIApplication alloc] init].buttons[#"Launch Test 1"];
void (^blockToMeasure)(void) = ^void(void) {
[launchTest1Button tap];
};
// Run once to warm up any potential caching properties
#autoreleasepool {
blockToMeasure();
}
// Now measure the block
[self measureBlock:blockToMeasure];
/// Collect the measured metrics and send somewhere.
When we run a test it prints:
measured [Time, seconds] average: 0.594, relative standard deviation: 0.517%, values: [0.602709, 0.593631, 0.593004, 0.592350, 0.596199, 0.593807, 0.591444, 0.593460, 0.592648, 0.592769],
If I could get the average time, that'd be sufficient for now.
Since there is no API to get this data you can pipe stderr stream and parse tests logs to get needed info e.g. average time. For instance you can use next approach:
#interface MeasureParser : NSObject
#property (nonatomic) NSPipe* pipe;
#property (nonatomic) NSRegularExpression* regex;
#property (nonatomic) NSMutableDictionary* results;
#end
#implementation MeasureParser
- (instancetype)init {
self = [super self];
if (self) {
self.pipe = NSPipe.pipe;
self.results = [NSMutableDictionary new];
let pattern = [NSString stringWithFormat:#"[^']+'\\S+\\s([^\\]]+)\\]'\\smeasured\\s\\[Time,\\sseconds\\]\\saverage:\\s([^,]+)"];
NSError* error = nil;
self.regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
if (error) {
return nil;
}
}
return self;
}
- (void)capture:(void (^)(void))block {
// Save original output
int original = dup(STDERR_FILENO);
setvbuf(stderr, nil, _IONBF, 0);
dup2(self.pipe.fileHandleForWriting.fileDescriptor, STDERR_FILENO);
__weak let wself = self;
self.pipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle *handle) {
var *str = [[NSString alloc] initWithData:handle.availableData encoding:NSUTF8StringEncoding];
let firstMatch = [wself.regex firstMatchInString:str options:NSMatchingReportCompletion range:NSMakeRange(0, str.length)];
if (firstMatch) {
let name = [str substringWithRange:[firstMatch rangeAtIndex:1]];
let average = [str substringWithRange:[firstMatch rangeAtIndex:2]];
wself.results[name] = average;
}
// Print to stdout because stderr is piped
printf("%s", [str cStringUsingEncoding:NSUTF8StringEncoding]);
};
block();
// Revert
fflush(stderr);
dup2(original, STDERR_FILENO);
close(original);
}
#end
How to use:
- (void)testPerformanceExample {
let measureParser = [MeasureParser new];
[measureParser capture:^{
[self measureBlock:^{
// Put the code you want to measure the time of here.
sleep(1);
}];
}];
NSLog(#"%#", measureParser.results);
}
// Outputs
{
testPerformanceExample = "1.001";
}
Swift 5 version
final class MeasureParser {
let pipe: Pipe = Pipe()
let regex: NSRegularExpression?
let results: NSMutableDictionary = NSMutableDictionary()
init() {
self.regex = try? NSRegularExpression(
pattern: "\\[(Clock Monotonic Time|CPU Time|Memory Peak Physical|Memory Physical|CPU Instructions Retired|Disk Logical Writes|CPU Cycles), (s|kB|kI|kC)\\] average: ([0-9\\.]*),",
options: .caseInsensitive)
}
func capture(completion: #escaping () -> Void) {
let original = dup(STDERR_FILENO)
setvbuf(stderr, nil, _IONBF, 0)
dup2(self.pipe.fileHandleForWriting.fileDescriptor, STDERR_FILENO)
self.pipe.fileHandleForReading.readabilityHandler = { [weak self] handle in
guard self != nil else { return }
let data = handle.availableData
let str = String(data: data, encoding: .utf8) ?? "<Non-ascii data of size\(data.count)>\n"
self!.fetchAndSaveMetrics(str)
// Print to stdout because stderr is piped
if let copy = (str as NSString?)?.cString(using: String.Encoding.utf8.rawValue) {
print("\(copy)")
}
}
completion()
fflush(stderr)
dup2(original, STDERR_FILENO)
close(original)
}
private func fetchAndSaveMetrics(_ str: String) {
guard let mRegex = self.regex else { return }
let matches = mRegex.matches(in: str, options: .reportCompletion, range: NSRange(location: 0, length: str.count))
matches.forEach {
let nameIndex = Range($0.range(at: 1), in: str)
let averageIndex = Range($0.range(at: 3), in: str)
if nameIndex != nil && averageIndex != nil {
let name = str[nameIndex!]
let average = str[averageIndex!]
self.results[name] = average
}
}
}
}
How to use it:
import XCTest
final class MyUiTests: XCTestCase {
var app: XCUIApplication!
let measureParser = MeasureParser()
// MARK: - XCTestCase
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
override func tearDown() {
//FIXME: Just for debugging
print(self.measureParser.results)
print(self.measureParser.results["CPU Cycles"])
print(self.measureParser.results["CPU Instructions Retired"])
print(self.measureParser.results["CPU Time"])
print(self.measureParser.results["Clock Monotonic Time"])
print(self.measureParser.results["Disk Logical Writes"])
print(self.measureParser.results["Memory Peak Physical"])
print(self.measureParser.results["Memory Physical"])
}
// MARK: - Tests
func testListing() {
self.measureParser.capture { [weak self] in
guard let self = self else { return }
self.measureListingScroll()
}
}
// MARK: XCTest measures
private func measureListingScroll() {
measure(metrics: [XCTCPUMetric(), XCTClockMetric(), XCTMemoryMetric(), XCTStorageMetric()]) {
self.app.swipeUp()
self.app.swipeUp()
self.app.swipeUp()
}
}
}
There's a private instance variable __perfMetricsForID of XCTestCase store the result.
And you can access it by call
NSDictionary* perfMetrics = [testCase valueForKey:#"__perfMetricsForID"];
the result is just like this:

IOS::How to get the MPMediaquery Songsquery Artwork

I'm using this code for getting the Artwork, but it's not workout for me. What's the wrong in this code.Suggest me.
Thanks.
MPMediaQuery *mySongsQuery = [MPMediaQuery songsQuery];
NSArray *SongsList = [mySongsQuery collections];
for (MPMediaItemCollection *SongsArt in SongsList) {
NSArray *songs = [SongsArt items];
for (MPMediaItem *song in songs) {
if ([(MPMediaItem*)item valueForProperty:MPMediaItemPropertyAssetURL] != nil) {
CGSize artworkImageViewSize = CGSizeMake(40, 40);
MPMediaItemArtwork *artwork = [song valueForProperty:MPMediaItemPropertyArtwork];
UIImage * image = [artwork imageWithSize:artworkImageViewSize];
if (image!= nil)
{
imgv_songImageView.image = image;
}
else
{
imgv_songImageView.image = [UIImage imageNamed:#"musicD-jpeg.png"];
}
}
}
I assume you just want to loop through all songs in the music library so I don't see a need for collections:
MPMediaQuery *mySongsQuery = [MPMediaQuery songsQuery];
for (MPMediaItem *item in mySongsQuery.items) {
if (![[item valueForProperty:MPMediaItemPropertyIsCloudItem]boolValue]) {
CGSize artworkImageViewSize = CGSizeMake(40, 40);
MPMediaItemArtwork *artwork = [song valueForProperty:MPMediaItemPropertyArtwork];
UIImage *image = [artwork imageWithSize:artworkImageViewSize];
if (image) {
imgv_songImageView.image = image;
} else {
imgv_songImageView.image = [UIImage imageNamed:#"musicD-jpeg.png"];
}
}
}
I'm not sure why you want to check for the Asset URL but I've left it in.
Here i am posting the code to get the tracks and sorting them alphabetically. Its written in swift3.
/// Get all the songs in the device and display in the tableview
///
func getAllSongs() {
let query: MPMediaQuery = MPMediaQuery.songs()
let allSongs = query.items
allSongItems?.removeAll()
guard allSongs != nil else {
return
}
var index = 0
for item in allSongs! {
let pathURL: URL? = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
if pathURL == nil {
print("#Warning!!! Track : \(item) is not playable.")
} else {
let trackInfo = SongItem()
trackInfo.index = index
trackInfo.mediaItem = item
let title = item.value(forProperty: MPMediaItemPropertyTitle) as? String ?? "<Unknown>"
let artistName = item.value(forProperty: MPMediaItemPropertyArtist) as? String ?? "<Unknown>"
trackInfo.songName = title
trackInfo.artistName = artistName
trackInfo.isSelected = false
trackInfo.songURL = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
allSongItems?.append(trackInfo)
index += 1
}
}
// Sort the songs alphabetically
let sortedArray: [SongItem]? = allSongItems?.sorted {
$0.songName!.localizedCompare($1.songName!) == .orderedAscending
}
allSongItems?.removeAll()
if let arr = sortedArray {
allSongItems?.append(contentsOf: arr)
}
}

Restkit: methodSignatureForSelector crashing in RKMappingOperation with EXC_BAD_ACCESS

In the file RKMappingOperation there is method
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
return [self.object methodSignatureForSelector:selector];
}
which triggers exception on iPhone5 but the application continues to run. But on iPhone4S the application crashes with EXC_BAD_ACCESS.
I assume the exception is handled as stated in the documentation:
Raises NSInvalidArgumentException. Override this method in your concrete subclass to return a proper NSMethodSignature object for the given selector and the class your proxy objects stand in for.
The log file:
T restkit.object_mapping:RKMappingOperation.m:862 Performing mapping operation: <RKMappingOperation 0x181e1960> for 'TSNDecimal' object. Mapping values from object 10 to object <TSNDecimal: 0x18004bc0> (entity: TSNDecimal; id: 0x1819dcb0 <x-coredata:///TSNDecimal/t507A4693-CB2E-4194-966A-00BA8AD5CC70391> ;
data: {
number = 0;
responseName = nil;
sortId = 0;
ticket = nil;
}) with object mapping (null)
2014-07-07 13:17:50.194 VZN Mobile[440:8f0f] D restkit.object_mapping:RKPropertyInspector.m:131 Cached property inspection for Class 'TSNDecimal': {
number = {
isPrimitive = 0;
keyValueCodingClass = NSDecimalNumber;
name = number;
};
responseName = {
isPrimitive = 0;
keyValueCodingClass = NSString;
name = responseName;
};
sortId = {
isPrimitive = 0;
keyValueCodingClass = NSNumber;
name = sortId;
};
sortIdValue = {
isPrimitive = 1;
keyValueCodingClass = NSNumber;
name = sortIdValue;
};
ticket = {
isPrimitive = 0;
keyValueCodingClass = TSNTicketApiTO;
name = ticket;
};
}
2014-07-07 13:17:50.194 VZN Mobile[440:9f07] T restkit.object_mapping:RKMappingOperation.m:440 Found transformable value at keyPath '(null)'. Transforming from class '__NSCFNumber' to 'NSDecimalNumber'
2014-07-07 13:17:50.204 VZN Mobile[440:9f07] self.object:10
Thank you Wain for the comment. Actually it was caused by a wrong code in Restkit transforming NSDecimalNumber to NSNumber. There is a method in RKValueTransformers.m:
+ (instancetype)decimalNumberToNumberValueTransformer
{
....
....
RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputValueClass, (#[ [NSNumber class], [NSDecimalNumber class]]), error);
if ([inputValue isKindOfClass:[NSNumber class]]) {
*outputValue = [NSDecimalNumber decimalNumberWithDecimal:[inputValue decimalValue]];
} else if ([inputValue isKindOfClass:[NSDecimalNumber class]]) {
*outputValue = inputValue;
}
return YES;
}];
}
The conditions have to be swapped this way:
if ([inputValue isKindOfClass:[NSDecimalNumber class]]) {
*outputValue = [NSDecimalNumber decimalNumberWithDecimal:[inputValue decimalValue]];
} else if ([inputValue isKindOfClass:[NSNumber class]]) {
*outputValue = inputValue;
}
Then the code is not crashing and it is working as author originally assumed.

How do I parse through an array of objects in Objective-C?

Coming from C++, here's my question :
I have created objects of this type :
Size *one = [[Size alloc] initWithX: 3 andY: 1];
Size *two = [[Size alloc] initWithX: 4 andY: 7];
// etc...
Size *thirtythree = [[Size alloc] initWithX: 5 andY: 9];
( with a #property int x; & #property int y; for each object.. )
that I have stored in an array as follows :
NSArray *arrayOfSizes;
arrayOfSizes = [NSArray arrayWithObjects:one,two,three,four,five,six,
seven,eight,nine,ten,eleven,twelve,thirteen,
fourteen,fifteen,sixteen,seventeen,eighteen,
nineteen,twenty,twentyone,twentytwo,
twentythree,twentyfour,twentyfive,twentysix,
twentyseven,twentyeight,twentynine,thirty,
thirtyone,thirtytwo,thirtythree nil];
now I have a single object of type :
Myobject *myObject = [[Myobject alloc] initWithX: 5 andY: 3];
that also has a #property int x; & #property int y; ...
and I want to compare its values to the values of the objects found in the array, until I find an array object of similar values.. But I don't know how to do that in Obj-C. (in c++ I would simply use a vector v; with v.size(); and v[x]; ..etc... I suppose..)
here's what I'm looking for.. :)
while( !wholeOfArrayOfSizesChecked && !found)
{
if ( // x & y of object in array is equal to x & y of myObject )
{
found = YES;
}
else if( // whole of array checked)
{
wholeOfArrayOfSizesChecked = YES;
}
else
{
//move on to the next object of the array..
}
}
Thanks in advance for any help!
Well, you could just use fast enumeration on the array. Something like this:
Myobject *myObject = [[Myobject alloc] initWithX: 5 andY: 3];
for (Size *s in arrayOfSizes)
{
if (s.x == myObject.x && s.y == myObject.y)
{
// Found one
// Do something useful...
break;
}
}
Another one:
NSUInteger index = [arrayOfSizes indexOfObjectPassingTest:
^BOOL(Size *s, NSUInteger idx, BOOL *stop)
{
return (s.x == myObject.x) && (s.y == myObject.y);
}
];
if (index != NSNotFound) {
id object = [arrayOfSizes objectAtIndex:index];
}
Just to use your given structure. There are smarter ways of doing it though :)
wholeOfArrayOfSizesChecked = NO;
int currObj = 0
while( !wholeOfArrayOfSizesChecked && !found)
{
Size *current = (Size *)[arrayOfSizes objectAtIndex:i];
if (myObject.x == current.x && myObject.y == current.y)
{
found = YES;
}
else if(currObj == [arrayOfSizes count] -1 )
{
wholeOfArrayOfSizesChecked = YES;
}
else
{
currObj++;
}
}
Try something like this:
for (int i = 0; i < [arrayOfSizes size]; i++)
{
Size *current = (Size *)[arrayOfSizes objectAtIndex:i];
if (myObject.x == current.x && myObject.y == current.y)
{
// found
break;
}
}
How'bout a for-in loop?
for (Size *item in array) {
// compare 'item' to myObject
if (/* equal condition here */) break;
}
-(BOOL) isSize:(Size*)size equalToMyObject:(MyObject*)object{
return (size.x == object.x) && (size.y == object.y);
}
//In some method where you are checking it:
for (Size* size in arrayOfSizes){
if ([self isSize:size equalToMyObject:myObject]){
//You found it! They're equal!
break;
}
}

NSCFString leak inVolving NSString

I am getting leak at:
NSString *firstNameStr = [NSString stringWithFormat:#"%s",firstNameString];
CODE:
+(NSString *)getValueForProperty:(ABPropertyID)propertyId
forContact:(NSString *)contactId
{
if (addressBook == nil)
{
addressBook = ABAddressBookCreate();
}
ABRecordID contactIntId = [contactId intValue];
ABRecordRef person;
person = ABAddressBookGetPersonWithRecordID(addressBook, contactIntId);
CFStringRef firstName;
char *firstNameString;
firstName = ABRecordCopyValue(person, propertyId);
// Paso a char* los datos para que se puedan escribir
static char* fallback = "";
int fbLength = strlen(fallback);
int firstNameLength = fbLength;
bool firstNameFallback = true;
if (firstName != NULL)
{
firstNameLength = (int) CFStringGetLength(firstName);
firstNameFallback = false;
}
if (firstNameLength == 0)
{
firstNameLength = fbLength;
firstNameFallback = true;
}
firstNameString = malloc(sizeof(char)*(firstNameLength+1));
if (firstNameFallback == true)
{
strcpy(firstNameString, fallback);
}
else
{
CFStringGetCString(firstName, firstNameString,
10*CFStringGetLength(firstName), kCFStringEncodingASCII);
}
if (firstName != NULL)
{
CFRelease(firstName);
}
NSString *firstNameStr = [NSString stringWithFormat:#"%s",firstNameString];
free(firstNameString);
return firstNameStr;
}
That means that the object allocated at that point is leaked. In this case, most likely because you over-retained it somewhere and failed to release it.
You need to carefully examine the lifespan of that particular string and figure out where you might be overwriting the reference without a release.
Build & Analyze might help considerably.