#100DaysOfCode - Antique

Listed below are my entries to #100DaysOfCode

Day 1: Distance between two CLLocation objects.

The code below is written in Swift and as of Xcode 11.5 and iOS 13.5.x is still correct.

func distance(me: CLLocation, other: CLLocation) -> Double {
    return me.distance(from: other) / 1000 // km
}

func distance(from location: CLLocation) -> CLLocationDistance returns the value in metres and can be divided by 1000 to return kilometres or 1609.34 to return miles.

Day 2: Generating a random password

The code below is written in Python and as of Python 3.8.x is still correct.

def generatePassword():
    lowercase = "abcdefghijklmnopqrstuvwxyz"
    uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    numbers = "1234567890"
    special = "!@#$%^&*()[]{}-_=+|;:'""',.<>/?"


    password_length = input("Enter desired password length:\n")
    length = int(password_length)

    allow_special = input("Allow special characters? (Y/N)\n")
    allow_special_chars = str(allow_special)


    password = ""
    for char in range(length):
        if allow_special_chars.upper() == "N":
            password += random.choice(lowercase + uppercase + numbers)
        else:
            password += random.choice(lowercase + uppercase + numbers + special)

    print(password)


# usage: generatePassword()

Day 3: Tweet a Reddit post with a specific flair

The code below is written in Python and as of Python 3.8.x is still correct.

import tweepy
import praw
import re
from tinydb import TinyDB, where

pattern = "FLAIR_NAME"


def getDatabase():
    print('Loading Database...')
    database = TinyDB("/path/to/database.json")
    print('Loaded Database...')
    return database

def saveSubmission(submission):
    print('Saving Submission ID...')
    database = getDatabase()
    database.insert({
        'submission_id': submission
    })
    print('Saved Submission ID...')


def loginReddit():
    print('Logging in... (Reddit)')
    reddit = praw.Reddit(client_id='', client_secret='', user_agent='')
    print('Logged in... (Reddit)')
    return reddit

def loginTwitter():
    print('Logging in... (Twitter)')
    twitter_auth_keys = {
        "consumer_key" : "",
        "consumer_secret" : "",
        "access_token" : "",
        "access_token_secret" : ""
    }

    auth = tweepy.OAuthHandler(twitter_auth_keys['consumer_key'], twitter_auth_keys['consumer_secret'])
    auth.set_access_token(twitter_auth_keys['access_token'], twitter_auth_keys['access_token_secret'])
    api = tweepy.API(auth)
    print('Logged in... (Twitter)')
    return api


def postTweet(submission):
    print('Posting Tweet...')
    twitter = loginTwitter()
    twitter.update_status('New Release - ' + submission.title + '\n' + submission.url)


def connectSubreddit():
    subreddit = loginReddit().subreddit('SUBREDDIT_NAME')
    return subreddit


def getSubmissions():
    subreddit = connectSubreddit()
    print('Connected to /r/' + str(subreddit))
    print('Getting submissions...')
    for submission in subreddit.new(limit=None):
        if re.search(pattern, submission.link_flair_text):
            if not getDatabase().search(where('submission_id') == submission.id):
                saveSubmission(submission.id)
                postTweet(submission)
                print('Found Post: ' + submission.id)
                print(submission.title)
            else:
                print('No Post Found')


# usage: getSubmissions()

Day 4: Corners as round as Kim's...

The code below is written in Swift and as of Xcode 11.5 and iOS 13.5.x is still correct.

extension UIView {
    func round(corners: UIRectCorner, radius: CGFloat) {
        let path = UIBezierPath(roundedRect: self.bounds, 
        byRoundingCorners: corners, cornerRadii:CGSize(width: radius, height: radius))
        
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        layer.mask = mask
    }
}


// usage: view.round(corners: .allCorners, radius: 14)

This produces a clean corner compared to the normal cornerRadius which does not perform so well. Example from @nathangitter on Twitter. Taken from my writeup here.

Day 5: Cutting a transparent hole in a UIVisualEffectView

The code below is written in Objective-C and as of Xcode 11.5 and iOS 13.5.x is still correct.

-(void) cut:(UIView *)view rect:(CGRect)rect usesRadius:(BOOL)usesRadius radius:(CGFloat)radius {
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:view.bounds];
    
    UIBezierPath *hole = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:usesRadius ? radius : 0];
    [path appendPath:hole];
    
    [path setUsesEvenOddFillRule:YES];
    
    CAShapeLayer *mask = [[CAShapeLayer alloc] init];
    [mask setFillRule:kCAFillRuleEvenOdd];
    [mask setFillColor:[[UIColor blackColor] CGColor]];
    [mask setPath:[path CGPath]];
    [view.layer setMask:mask]; 
}

// usage: [self cut:someView rect:CGRectMake(8, 8, 20, 20) usesRadius:YES radius:8];

The idea for this came when looking at Apple's Control Centre toggles, most have a transparent toggle with a slight blur, writing this in Carbonite (a project of mine) lead to this code snippet.

Day 6: Logging NSString objects to a file

The code below is written in Objective-C and as of Xcode 11.5 and iOS 13.5.x is still correct.

-(void) log:(NSString *)string {
    NSString *path = // path goes here;
    if(![[NSFileManager defaultManager] fileExistsAtPath:path]) {
        [[NSData data] writeToFile:path atomically:YES];
    }
    
    NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:path];
    [handle truncateFileAtOffset:[handle seekToEndOfFile]];
    [handle writeData:[string dataUsingEncoding:NSUTF8StringEncoding]];
    [handle closeFile];
}


// usage: [self log:@"Look at me! I'm your new logging method."];

Day 7: Using MPMusicPlayerController methods

The code below is written in Objective-C and as of Xcode 11.5 and iOS 13.5.x is still correct.

// Creating an MPMusicPlayerController instance
MPMusicPlayer *musicPlayerController = [MPMusicPlayerController systemMusicPlayer];


// Getting an array of all songs
NSArray <MPMediaItem *> *songs = [[MPMediaQuery songsQuery] items];


// Getting song information (below is if songs.count > 0)
NSString *songTitle = [songs[0] valueForKey:MPMediaItemPropertyTitle];
// For more keys visit this link to view the official documentation: 
// https://developer.apple.com/documentation/mediaplayer/mpmediaitem?language=objc


// Play a song
MPMediaItem *songToPlay = songs[0]

[musicPlayerController setNowPlayingItem:item];

[musicPlayerController prepareToPlay];
[musicPlayerController play];


// Play
[musicPlayerController play];


// Pause
[musicPlayerController pause];


// Skip
[musicPlayerController skipToNextItem];


// Previous
[musicPlayerController skipToPreviousItem];

Day 8: Writing an API wrapper for weatherstack.com

The code below is written in Objective-C and as of Xcode 11.5 and iOS 13.5.x is still correct.

// WeatherData.h

@interface WeatherData : NSObject
// REQUEST
@property (nonatomic, strong) NSString *type;
@property (nonatomic, strong) NSString *query;
@property (nonatomic, strong) NSString *language;
@property (nonatomic, strong) NSString *unit;

// LOCATION
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *country;
@property (nonatomic, strong) NSString *region;
@property (nonatomic, strong) NSString *lat;
@property (nonatomic, strong) NSString *lon;
@property (nonatomic, strong) NSString *timezone_id;
@property (nonatomic, strong) NSString *localtime;
@property (nonatomic, strong) NSString *localtime_epoch;
@property (nonatomic, strong) NSString *utc_offset;

// CURRENT
@property (nonatomic, strong) NSString *observation_time;
@property (nonatomic, strong) NSString *temperature;
@property (nonatomic, strong) NSString *weather_code;
@property (nonatomic, strong) NSArray <NSString *> *weather_icons;
@property (nonatomic, strong) NSArray <NSString *> *weather_descriptions;

@property (nonatomic, strong) NSNumber *wind_speed;
@property (nonatomic, strong) NSNumber *wind_degree;
@property (nonatomic, strong) NSNumber *wind_dir;
@property (nonatomic, strong) NSNumber *pressure;
@property (nonatomic, strong) NSNumber *precip;
@property (nonatomic, strong) NSNumber *humidity;
@property (nonatomic, strong) NSNumber *cloudcover;
@property (nonatomic, strong) NSNumber *feelslike;
@property (nonatomic, strong) NSNumber *uv_index;
@property (nonatomic, strong) NSNumber *visibility;

@property (nonatomic, assign) BOOL is_day;

-(id) initWithJSON:(NSData *)json;
@end
// WeatherData.m

#import "WeatherData.h"

@implementation WeatherData
-(id) initWithJSON:(NSData *)json {
  if(self = [super init]) {
    NSError *error;
    id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
    if(error) {
      NSLog(@"NSJSONSerialization failed with error: %@", [error localizedDescription]);
      return self;
    }

    if([jsonObject isKindOfClass:[NSDictionary class]]) {
      id requestDictionary = [jsonObject objectForKey:@"request"];
      if([requestDictionary isKindOfClass:[NSDictionary class]]) {
        id type = [requestDictionary objectForKey:@"type"];
        if([type isKindOfClass:[NSString class]]) {
          _type = type;
        }

        id query = [requestDictionary objectForKey:@"query"];
        if([query isKindOfClass:[NSString class]]) {
          _query = query;
        }

        id language = [requestDictionary objectForKey:@"type"];
        if([language isKindOfClass:[NSString class]]) {
          _language = language;
        }

        id unit = [requestDictionary objectForKey:@"unit"];
        if([unit isKindOfClass:[NSString class]]) {
          _unit = unit;
        }
      }

      id locationDictionary = [jsonObject objectForKey:@"location"];
      if([locationDictionary isKindOfClass:[NSDictionary class]]) {
        id name = [locationDictionary objectForKey:@"name"];
        if([name isKindOfClass:[NSString class]]) {
          _name = name;
        }

        id country = [locationDictionary objectForKey:@"country"];
        if([country isKindOfClass:[NSString class]]) {
          _country = country;
        }

        id region = [locationDictionary objectForKey:@"region"];
        if([region isKindOfClass:[NSString class]]) {
          _region = region;
        }

        id lat = [locationDictionary objectForKey:@"lat"];
        if([lat isKindOfClass:[NSString class]]) {
          _lat = lat;
        }

        id lon = [locationDictionary objectForKey:@"lon"];
        if([lon isKindOfClass:[NSString class]]) {
          _lon = lon;
        }

        id timezone_id = [locationDictionary objectForKey:@"timezone_id"];
        if([timezone_id isKindOfClass:[NSString class]]) {
          _timezone_id = [timezone_id stringByReplacingOccurrencesOfString:@""];
        }

        id localtime = [locationDictionary objectForKey:@"localtime"];
        if([localtime isKindOfClass:[NSString class]]) {
          _localtime = localtime;
        }

        _localtime_epoch = [NSNumber numberWithInt:[[locationDictionary objectForKey:@"localtime_epoch"] intValue]];

        id utc_offset = [locationDictionary objectForKey:@"utc_offset"];
        if([utc_offset isKindOfClass:[NSString class]]) {
          _utc_offset = utc_offset;
        }
      }

      id currentDictionary = [jsonObject objectForKey:@"current"];
      if([currentDictionary isKindOfClass:[NSDictionary class]]) {
        id observation_time = [locationDictionary objectForKey:@"observation_time"];
        if([observation_time isKindOfClass:[NSString class]]) {
          _observation_time = observation_time;
        }

        _temperature = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"temperature"] intValue]];
        _weather_code = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"weather_code"] intValue]];

        id weather_icons = [currentDictionary objectForKey:@"weather_icons"];
        if([weather_icons isKindOfClass:[NSArray class]]) {
          _weather_icons = weather_icons;
        }

        id weather_descriptions = [currentDictionary objectForKey:@"weather_descriptions"];
        if([weather_descriptions isKindOfClass:[NSArray class]]) {
          _weather_descriptions = weather_descriptions;
        }

        _wind_speed = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"wind_speed"] intValue]];
        _wind_degree = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"weather_degree"] intValue]];

        id wind_dir = [currentDictionary objectForKey:@"wind_dir"];
        if([wind_dir isKindOfClass:[NSString class]]) {
          _wind_dir = wind_dir;
        }

        _pressure = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"pressure"] intValue]];
        _precip = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"precip"] intValue]];
        _humidity = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"humidity"] intValue]];
        _cloudcover = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"cloudcover"] intValue]];
        _feelslike = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"feelslike"] intValue]];
        _uv_index = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"uv_index"] intValue]];
        _visibility = [NSNumber numberWithInt:[[currentDictionary objectForKey:@"visibility"] intValue]];

        _visibility = [NSNumber numberWithBool:[[currentDictionary objectForKey:@"is_day"] boolValue]];
      }
    } else {
      NSLog(@"Data is not a dictionary");
    }
  } return self;
}
@end
// WeatherAPI.h

#import <CoreLocation/CoreLocation.h>
#import "WeatherData.h"

@interface WeatherAPI : NSObject
+(WeatherAPI *)sharedInstance;

-(void) fetchWeatherForLocation:(CLLocation *)location completion:(void(^)(WeatherData *weatherData))completion failure:(void(^)(NSError *error))failure;
@end
// WeatherAPI.m

#import "WeatherAPI.h"

@implementation WeatherAPI
+(WeatherAPI *) sharedInstance {
  static WeatherAPI *sharedInstance = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sharedInstance = [[WeatherAPI alloc] init];
  });
  return sharedInstance;
}

-(void) fetchWeatherForLocation:(CLLocation *)location completion:(void(^)(WeatherData *weatherData))completion failure:(void(^)(NSError *error))failure {
  float latitude = location.coordinate.latitude;
  float longitude = location.coordinate.longitude;

  NSString *urlString = [NSString stringWithFormat:@"http://api.weatherstack.com/current?access_key=%@&query=%f,%f&units=%@", @"35c9be78ab2dcc41d19c5879147894d0", latitude, longitude, @"m"];

  NSURLSession *session = [NSURLSession sharedSession];
  NSURL *url = [NSURL URLWithString:urlString];

  [[session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if(error) {
      failure(error);
    } else {
      WeatherData *weather = [[WeatherData alloc] initWithJSON:data];
      completion(weather);
    }
  }] resume];
}
@end

The code above is the first available wrapper for https://weatherstack.com written in Objective-C. Developed by me, @antique_dev.

Day 9: Convert NSTimeInterval to NSString

The code below is written in Objective-C and as of Xcode 11.5 and iOS 13.5.x is still correct.

-(NSString *) string:(NSTimeInterval)timeInterval {
    NSInteger interval = timeInterval;
    // hours: long hours (interval / 3600);
    long minutes = interval % 60;
    long seconds = (interval / 60) % 60;
    
    return [NSString stringWithFormat:@"%0.2ld:%0.2ld", minutes, seconds];
}

Day 10: Convert NSDate to NSString and vice versa

The code below is written in Objective-C and as of Xcode 11.5 and iOS 13.5.x is still correct.

-(NSString *) stringFromDate:(NSDate *)date {
    NSDateFormatter *formatter =  [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"dd/MM/yyyy h:mma"];
    return [formatter stringFromDate:date];
}


// returns 15/05/2020 12:00am (or whatever the provided NSDate contains)
-(NSDate *) dateFromString:(NSString *)string {
    NSDateFormatter *formatter =  [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"dd/MM/yyyy h:mma"];
    return [formatter dateFromString:string];
}

Day 11: Achieving different blurs in Logos

The code below is written in Objective-C (Logos) and as of iOS 13.5.x is still correct.

@interface MTMaterialView : UIView
+(id) materialViewWithRecipe:(long long)arg1 configuration:(long long)arg2;
@end


/* usage:
    MTMaterialView *blurView = [MTMaterialView materialViewWithRecipe:0 configuration:0];
    [blurView setFrame:CGRectMake(x, y, w, h)];
    [self addSubview:blurView];
*/
@interface _UIBackdropView : UIView
-(id) initWithFrame:(CGRect)arg1 autosizesToFitSuperview:(BOOL)arg2 settings:(id)arg3;
-(id) initWithPrivateStyle:(int)arg1;
-(id) initWithSettings:(id)arg1;
-(id) initWithStyle:(int)arg1;
	
// Some methods here may cause crashes and should be removed, test by running each method if wanting to use them.
-(void) setBlurFilterWithRadius:(float)arg1 blurQuality:(id)arg2 blurHardEdges:(int)arg3;
-(void) setBlurFilterWithRadius:(float)arg1 blurQuality:(id)arg2;
-(void) setBlurHardEdges:(int)arg1;
-(void) setBlurQuality:(id)arg1;
-(void) setBlurRadius:(float)arg1;
-(void) setBlurRadiusSetOnce:(BOOL)arg1;
-(void) setBlursBackground:(BOOL)arg1;
-(void) setBlursWithHardEdges:(BOOL)arg1;
@end

@interface _UIBackdropViewSettings : NSObject
+(id) settingsForStyle:(int)arg1;
@end


/* usage:
    _UIBackdropView *blurView = [[_UIBackdropView alloc] initWithStyle:2060];
    [blurView setFrame:CGRectMake(x, y, w, h)];
    [blurView setBlurRadiusSetOnce:NO];
    [blurView setBlurRadius:4.0];
    [blurView setBlurHardEdges:2];
    [blurView setBlursWithHardEdges:YES];
    [blurView setBlurQuality:@"default"];
    [view addSubview:blurView];
*/

Additional styles and further documentation can be found here.

Some methods may no longer work in newer iOS versions, if this is the case ensure you remove them. The class itself does still work.

UIVisualEffectView *blurView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleRegular];
[blurView setFrame:CGRectMake(x, y, w, h)];
[self addSubview:blurView];

Additional styles and further documentation can be found here.

Day 12: NSFileManager and its many uses

The code below is written in Swift and as of Xcode 11.5 and iOS 13.5.x is still correct.

// Check if a file or folder exists
let fileExists = FileManager.default.fileExists(atPath: "PATH")

var isDirectory = false
let folderExists = FileManager.default.fileExists(atPath: "PATH", isDirectory: &isDirectory)


// Copy item from one path to another
FileManager.default.copyItem(atPath: "PATH", toPath: "PATH")


// Move item from one path to another
FileManager.default.moveItem(atPath: "PATH", toPath: "PATH")


// Remove item from the provided path
FileManager.default.removeItem(atPath: "PATH")


// List an array of contents from the provided path
let filesArray = FileManager.default.contentsOfDirectory(atPath: "PATH")


// Create a symbolic link
FileManager.default.createSymbolicLink(atPath: "PATH", withDestinationPath: "PATH")


// Get the name of a file or folder at the provided path
let fileName = FileManager.default.displayName(atpath: "PATH")

Further documentation can be found here.

Day 13: Displaying a window above SpringBoard (iOS 13 and below).

The code below is written in Objective-C and as of iOS 13.5.x is still correct.

// Our window will be called 'window' for this snippet.

-(void) showWindow {
    [window setHidden:NO];
    
    if(@available(iOS 13.0, *)) {
        if(!window.windowScene || window.windowScene.activationState != UISceneActivationStateForegroundActive) {
            for(UIScene *scene in [[UIApplication sharedApplication] connectedScenes]) {
                if(scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
                    window.windowScene = (UIWindowScene *)scene;
                    break;
                }
            }
        }
    } else {
        // Normal iOS 12 and below methods work here
    }
}

Remember to set the windowLevel, a good level is 1089 which shows the window above everything while not being too high.

Day 14: Compressing and decompressing data

The code below is written in Swift and as of iOS 13.5.x is still correct.

// Compress
do {
  let compressed = try (data as NSData).compress(using: .lzma)
  // Save compressed data
} catch {
  print(error.localizedDescription)
}

// Decompress
do {
  let decompressed = try (data as NSData).decompress(using: .lzma)
  // Use decompressed data
} catch {
  print(error.localizedDescription)
}

Day 15: Getting an iOS device's UDID (Jailbroken)

The code below is written in Objective-C and as of iOS 12.x is still correct.

// main.m

#include <stdio.h>


OBJC_EXTERN CFStringRef MGCopyAnswer(CFStringRef key) WEAK_IMPORT_ATTRIBUTE;
NSString *udid() {
    CFStringRef udid = MGCopyAnswer(CFSTR("UniqueDeviceID"));
    return (NSString *)CFBridgingRelease(udid);
}


#define ANSI_COLOR_RED    "\x1b[31m"
#define ANSI_COLOR_CYAN    "\x1b[36m"
#define ANSI_COLOR_RESET    "\x1b[0m"


int main(int argc, char *argv[], char *envp[]) {
    NSError *error;
    [udid() writeToFile:@"/var/mobile/udid.txt" atomically:NO encoding:NSStringEncodingConversionAllowLossy error:&error];

    if(error) {
        printf("Error: %s%s%s\n", ANSI_COLOR_RED, [error.localizedDescription UTF8String], ANSI_COLOR_RESET);
    } else {
        printf("UDID: %s%s%s\n", ANSI_COLOR_CYAN, [udid() UTF8String], ANSI_COLOR_RESET);
        printf("%s%s%s\n", ANSI_COLOR_RED, [@"Saved to '/var/mobile/udid.txt'" UTF8String], ANSI_COLOR_RESET);
    } return 0;
}
<!-- entitlements.xml -->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>http://com.apple.private.MobileGestalt.AllowedProtectedKeys</key>
    <true/>
<key>http://com.apple.private.security.no-container</key>
<true/>
</dict>
</plist>

Last updated